diff --git a/spring-data-rest-hal-browser/pom.xml b/spring-data-rest-hal-browser/pom.xml index 835576b4b..9171b9f37 100644 --- a/spring-data-rest-hal-browser/pom.xml +++ b/spring-data-rest-hal-browser/pom.xml @@ -12,7 +12,8 @@ Spring Data REST - HAL Browser - b7669f1-1 + 9f96c74 + 0.7.21 @@ -33,7 +34,14 @@ org.webjars hal-browser - b7669f1-1 + ${browser.version} + provided + + + + org.webjars + json-editor + ${json-editor.version} provided @@ -49,7 +57,7 @@ 2.10 - unpack + unpack-hal-browser process-resources unpack @@ -64,6 +72,22 @@ ${project.build.directory}/hal-browser + + unpack-json-editor + process-resources + + unpack + + + + + org.webjars + json-editor + + + ${project.build.directory}/json-editor + + @@ -82,7 +106,10 @@ - + + + + diff --git a/spring-data-rest-hal-browser/src/main/resources/META-INF/spring-data-rest/hal-browser/index.html b/spring-data-rest-hal-browser/src/main/resources/META-INF/spring-data-rest/hal-browser/index.html new file mode 100644 index 000000000..80a752427 --- /dev/null +++ b/spring-data-rest-hal-browser/src/main/resources/META-INF/spring-data-rest/hal-browser/index.html @@ -0,0 +1,288 @@ + + + + The HAL Browser (customized for Spring Data REST) + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-data-rest-hal-browser/src/main/resources/META-INF/spring-data-rest/hal-browser/js/CustomPostForm.js b/spring-data-rest-hal-browser/src/main/resources/META-INF/spring-data-rest/hal-browser/js/CustomPostForm.js new file mode 100644 index 000000000..9c03b57a6 --- /dev/null +++ b/spring-data-rest-hal-browser/src/main/resources/META-INF/spring-data-rest/hal-browser/js/CustomPostForm.js @@ -0,0 +1,201 @@ +/** + * Custom Backbone view that uses JSON Schema metadata to create pop-up dialog with actual field names instead of + * asking user to input raw JSON. + * + * NOTE: Because JSON Schema lists all properties, including those that are links, they have to be filtered out. + * Links have to be set via a PUT operation with the proper media type. + * + * @author Greg Turnquist + * @since 2.4 + * @see DATAREST-627 + */ +/* jshint strict: true */ +/* globals HAL, Backbone, _, $, window, jqxhr */ + +'use strict'; + +var CustomPostForm = Backbone.View.extend({ + initialize: function (opts) { + this.href = opts.href.split('{')[0]; + this.vent = opts.vent; + _.bindAll(this, 'createNewResource'); + }, + + events: { + 'submit form': 'createNewResource' + }, + + className: 'modal fade', + + /** + * Perform a POST/PUT operation on the resource. + * + * @param e + */ + createNewResource: function (e) { + e.preventDefault(); + + var self = this; + + var opts = { + url: this.$('.url').val(), + headers: {'Content-Type': 'application/json'}, + method: this.$('.method').val(), + data: this.getNewResourceData() + }; + + HAL.client.request(opts).done(function (response) { + self.vent.trigger('response', {resource: response, jqxhr: jqxhr}); + }).fail(function (e) { + self.vent.trigger('fail-response', {jqxhr: jqxhr}); + }).always(function (e) { + self.vent.trigger('response-headers', {jqxhr: jqxhr}); + window.location.hash = 'NON-GET:' + opts.url; + }); + + this.$el.modal('hide'); + }, + + /** + * Draw the dialog after fetching the resource's JSON Schema metadata. If no metadata is available, use the + * fallback editor. + * + * @param opts + * @returns {CustomPostForm} + */ + render: function (opts) { + var self = this; + + try { + HAL.client.request({ + method: 'HEAD', + url: this.href + }).done(function (message, text, jqXHR) { + self.$el.html(self.template({href: self.href})); + + try { + var hal = self.w3cLinksToHalLinks(jqXHR.getResponseHeader('Link')); + + HAL.client.request({ + method: 'GET', + url: hal._links.profile.href, + headers: {'Accept': 'application/schema+json'} + }).done(function (schema) { + self.loadJsonEditor(schema); + }); + } catch (e) { + self.loadFallbackEditor(); + } + + self.$el.modal(); + }); + } catch (e) { + self.loadFallbackEditor(); + self.$el.modal(); + } + + + return this; + }, + + /** + * Load the JSON Schema-driven editor. + * + * @see https://github.com/jdorn/json-editor + */ + loadJsonEditor: function (schema) { + var self = this; + + /** + * Remove URI-based fields since this dialog doesn't handle relationships. + */ + Object.keys(schema.properties).forEach(function (property) { + if (schema.properties[property].hasOwnProperty('format') && + schema.properties[property].format === 'uri') { + delete schema.properties[property]; + } + }); + + /** + * See https://github.com/jdorn/json-editor#options for more customizing options. + */ + this.editor = new window.JSONEditor(this.$('#jsoneditor')[0], { + theme: 'bootstrap2', + schema: schema, + disable_collapse: true, + disable_edit_json: true, + disable_properties: true + }); + + this.getNewResourceData = function() { + return JSON.stringify(self.editor.getValue()); + } + }, + + /** + * Load fallback editor that doesn't depend on any form of metadata. + */ + loadFallbackEditor: function () { + var editor = this.$('#jsoneditor'); + + editor.append($('

' + this.href + '

')); + + var inputBox = $(''); + editor.append(inputBox); + + this.getNewResourceData = function() { + return inputBox.val(); + } + }, + + /** + * Convert a W3C link header into a HAL-based set of _links. + * + * e.g. + * ; rel="persons",; rel="profile" + * to + * { + * _links: { + * persons: { + * href: http://localhost:8080/persons + * }, + * profile: { + * href: http://localhost:8080/profile/persons + * } + * } + * } + * + * @param linkHeader - HTTP Response header containing a list of W3C compliant links with rels. + * @see http://www.w3.org/wiki/LinkHeader + */ + w3cLinksToHalLinks: function (linkHeader) { + var w3cLinks = linkHeader.split(','); + + var halLinks = {_links: {}}; + + w3cLinks.forEach(function (w3cLink) { + var parts = w3cLink.split(';'); + + var hrefWrappedWithBrackets = parts[0]; + var href = hrefWrappedWithBrackets.slice(1, parts[0].length - 1); + + var w3cRel = parts[1]; + var relWrappedWithQuotes = w3cRel.split('=')[1]; + var rel = relWrappedWithQuotes.slice(1, relWrappedWithQuotes.length - 1); + + halLinks._links[rel] = { "href": href }; + }); + + return halLinks; + }, + + /** + * Look up the HTML template. + */ + template: _.template($('#dynamic-request-template').html()) +}); + +/** + * Inject the form into the HAL Browser. + */ +HAL.customPostForm = CustomPostForm; \ No newline at end of file diff --git a/src/main/asciidoc/images/hal-browser-2.png b/src/main/asciidoc/images/hal-browser-2.png index e1fbe1f3d..6d9cac274 100644 Binary files a/src/main/asciidoc/images/hal-browser-2.png and b/src/main/asciidoc/images/hal-browser-2.png differ