You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/07/24 07:31:45 UTC

[1/2] incubator-brooklyn git commit: [BROOKLYN-132] Add versions dropdown when creating applications from catalog template

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master dd0ab7f3b -> 8ad732c84


[BROOKLYN-132] Add versions dropdown when creating applications from catalog template


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/133c3199
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/133c3199
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/133c3199

Branch: refs/heads/master
Commit: 133c31996a24f269a0ec8482b2bc3f1d577ef3da
Parents: ea1d6cb
Author: Thomas Bouron <th...@cloudsoftcorp.com>
Authored: Tue Jun 30 10:08:34 2015 +0100
Committer: Thomas Bouron <th...@cloudsoftcorp.com>
Committed: Thu Jul 23 17:43:50 2015 +0100

----------------------------------------------------------------------
 usage/jsgui/pom.xml                             |   2 +-
 .../assets/js/model/catalog-application.js      |  55 ++++++++
 .../assets/js/view/application-add-wizard.js    | 107 ++++++++++-----
 .../create-step-template-entry.html             |   2 +-
 .../app-add-wizard/deploy-version-option.html   |  23 ++++
 .../assets/tpl/app-add-wizard/deploy.html       |   7 +-
 .../specs/model/catalog-application-spec.js     | 130 +++++++++++++++++++
 .../fixtures/catalog-application-list.json      |  29 +++++
 .../resources/fixtures/catalog-application.json |   9 ++
 9 files changed, 327 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/133c3199/usage/jsgui/pom.xml
----------------------------------------------------------------------
diff --git a/usage/jsgui/pom.xml b/usage/jsgui/pom.xml
index 9280c6c..32f7f66 100644
--- a/usage/jsgui/pom.xml
+++ b/usage/jsgui/pom.xml
@@ -158,7 +158,7 @@
         </testResources>
         <plugins>
             <!--
-                 run js tests with: $ mvn clean process-resources jasmine:test
+                 run js tests with: $ mvn clean process-test-resources jasmine:test
                  run tests in the browser with: $ mvn jasmine:bdd
             -->
             <plugin>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/133c3199/usage/jsgui/src/main/webapp/assets/js/model/catalog-application.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/model/catalog-application.js b/usage/jsgui/src/main/webapp/assets/js/model/catalog-application.js
new file mode 100644
index 0000000..73b4d03
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/js/model/catalog-application.js
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+define(["underscore", "backbone"], function (_, Backbone) {
+    
+    var CatalogApplication = {}
+
+    CatalogApplication.Model = Backbone.Model.extend({
+        defaults: function () {
+            return {
+                id: "",
+                type: "",
+                name: "",
+                version: "",
+                description: "",
+                planYaml: "",
+                iconUrl: ""
+            }
+        }
+    })
+
+    CatalogApplication.Collection = Backbone.Collection.extend({
+        model: CatalogApplication.Model,
+        url: '/v1/catalog/applications',
+        getDistinctApplications: function() {
+            return this.groupBy('type');
+        },
+        getTypes: function(type) {
+            return _.uniq(this.chain().map(function(model) {return model.get('type')}).value());
+        },
+        hasType: function(type) {
+            return this.where({type: type}).length > 0;
+        },
+        getVersions: function(type) {
+            return this.chain().filter(function(model) {return model.get('type') === type}).map(function(model) {return model.get('version')}).value();
+        }
+    })
+
+    return CatalogApplication
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/133c3199/usage/jsgui/src/main/webapp/assets/js/view/application-add-wizard.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/application-add-wizard.js b/usage/jsgui/src/main/webapp/assets/js/view/application-add-wizard.js
index 09fd1c4..13e4cf0 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/application-add-wizard.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/application-add-wizard.js
@@ -22,7 +22,7 @@
  */
 define([
     "underscore", "jquery", "backbone", "brooklyn-utils", "js-yaml",
-    "model/entity", "model/application", "model/location",
+    "model/entity", "model/application", "model/location", "model/catalog-application",
     "text!tpl/app-add-wizard/modal-wizard.html",
     "text!tpl/app-add-wizard/create.html",
     "text!tpl/app-add-wizard/create-step-template-entry.html", 
@@ -30,14 +30,15 @@ define([
     "text!tpl/app-add-wizard/required-config-entry.html",
     "text!tpl/app-add-wizard/edit-config-entry.html",
     "text!tpl/app-add-wizard/deploy.html",
+    "text!tpl/app-add-wizard/deploy-version-option.html",
     "text!tpl/app-add-wizard/deploy-location-row.html",
     "text!tpl/app-add-wizard/deploy-location-option.html",
     "bootstrap"
     
-], function (_, $, Backbone, Util, JsYaml, Entity, Application, Location,
+], function (_, $, Backbone, Util, JsYaml, Entity, Application, Location, CatalogApplication,
              ModalHtml, CreateHtml, CreateStepTemplateEntryHtml, CreateEntityEntryHtml,
              RequiredConfigEntryHtml, EditConfigEntryHtml, DeployHtml,
-             DeployLocationRowHtml, DeployLocationOptionHtml
+             DeployVersionOptionHtml, DeployLocationRowHtml, DeployLocationOptionHtml
 ) {
 
     /** Special ID to indicate that no locations will be provided when starting the server. */
@@ -56,7 +57,7 @@ define([
     function oldSpecToCamp(spec) {
         var services;
         if (spec.type) {
-            services = [entityToCamp({type: spec.type, config: spec.config})];
+            services = [entityToCamp({type: spec.type, version: spec.version, config: spec.config})];
         } else if (spec.entities) {
             services = [];
             var entities = spec.entities;
@@ -80,6 +81,7 @@ define([
         var result = {};
         if (entity.name && (!options || !options.exclude_name)) result.name = entity.name;
         if (entity.type) result.type = entity.type;
+        if (entity.type && entity.version) result.type += ":" + entity.version;
         if (entity.config && _.size(entity.config)) result["brooklyn.config"] = entity.config;
         return result;
     }
@@ -95,6 +97,8 @@ define([
         },
         template:_.template(ModalHtml),
         initialize:function () {
+            this.catalog = {}
+            this.catalog.applications = {}
             this.model = {}
             this.model.spec = new Application.Spec;
             this.model.yaml = "";
@@ -105,7 +109,7 @@ define([
                               step_id:'what-app',
                               title:'Create Application',
                               instructions:'Choose or build the application to deploy',
-                              view:new ModalWizard.StepCreate({ model:this.model, wizard: this })
+                              view:new ModalWizard.StepCreate({ model:this.model, wizard: this, catalog: this.catalog })
                           },
                           {
                               // TODO rather than make this another step -- since we now on preview revert to the yaml tab
@@ -113,7 +117,7 @@ define([
                               step_id:'name-and-locations',
                               title:'<%= appName %>',
                               instructions:'Specify the locations to deploy to and any additional configuration',
-                              view:new ModalWizard.StepDeploy({ model:this.model })
+                              view:new ModalWizard.StepDeploy({ model:this.model, catalog: this.catalog })
                           }
                           ]
         },
@@ -344,7 +348,6 @@ define([
         initialize:function () {
             var self = this
             self.catalogEntityIds = []
-            self.catalogApplicationIds = []
 
             this.$el.html(this.template({}))
 
@@ -358,20 +361,23 @@ define([
                 self.catalogEntityIds = _.map(result, function(item) { return item.id })
                 self.$(".entity-type-input").typeahead().data('typeahead').source = self.catalogEntityIds
             })
-            // TODO use catalog-item-summary.js instead of raw json; see comments in that file
-            $.get('/v1/catalog/applications', {}, function (result) {
-                self.catalogApplicationItems = result
-                self.catalogApplicationIds = _.map(result, function(item) { return item.id })
-                self.$("#appClassTab .application-type-input").typeahead().data('typeahead').source = self.catalogApplicationIds
-                $('#catalog-applications-throbber').hide();
-                $('#catalog-applications-empty').hide();
-                if (self.catalogApplicationItems && self.catalogApplicationItems.length > 0) {
-                    self.addTemplateLozenges()
-                } else {
-                    $('#catalog-applications-empty').show();
-                    self.showYamlTab();
+            this.options.catalog.applications = new CatalogApplication.Collection();
+            this.options.catalog.applications.fetch({
+                data: $.param({
+                    allVersions: true
+                }),
+                success: function (collection, response, options) {
+                    self.$("#appClassTab .application-type-input").typeahead().data('typeahead').source = collection.getTypes();
+                    $('#catalog-applications-throbber').hide();
+                    $('#catalog-applications-empty').hide();
+                    if (collection.size() > 0) {
+                        self.addTemplateLozenges()
+                    } else {
+                        $('#catalog-applications-empty').show();
+                        self.showYamlTab();
+                    }
                 }
-            })
+            });
         },
         renderConfiguredEntities:function () {
             var $configuredEntities = this.$('#entitiesAccordionish').empty()
@@ -436,17 +442,18 @@ define([
         },
         addTemplateLozenges: function(event) {
             var that = this
-            _.each(this.catalogApplicationItems, function(item) {
-                that.addTemplateLozenge(that, item)
+            _.each(this.options.catalog.applications.getDistinctApplications(), function(item) {
+                that.addTemplateLozenge(that, item[0])
             })
         },
         addTemplateLozenge: function(that, item) {
             var $tempel = _.template(CreateStepTemplateEntryHtml, {
-                id: item.id,
-                name: item.name || item.id,
-                description: item.description,
-                planYaml:  item.planYaml,
-                iconUrl: item.iconUrl
+                id: item.get('id'),
+                type: item.get('type'),
+                name: item.get('name') || item.get('id'),
+                description: item.get('description'),
+                planYaml:  item.get('planYaml'),
+                iconUrl: item.get('iconUrl')
             })
             $("#create-step-template-entries", that.$el).append($tempel)
         },
@@ -457,9 +464,10 @@ define([
             if (!wasSelected) {
                 $tl.addClass("selected")
                 this.selectedTemplate = {
-                    type: $tl.attr('id'),
+                    id: $tl.attr('id'),
+                    type: $tl.data('type'),
                     name: $tl.data("name"),
-                    yaml: $tl.data("yaml")
+                    yaml: $tl.data("yaml"),
                 };
                 if (this.selectedTemplate.yaml) {
                     $("textarea#yaml_code").val(this.selectedTemplate.yaml);
@@ -509,10 +517,11 @@ define([
         saveTemplate:function () {
             if (!this.selectedTemplate) return false
             var type = this.selectedTemplate.type;
-            if (!_.contains(this.catalogApplicationIds, type)) {
+            if (!this.options.catalog.applications.hasType(type)) {
                 $('.entity-info-message').show('slow').delay(2000).hide('slow')
                 return false
             }
+
             this.model.spec.set("type", type);
             this.model.name = this.selectedTemplate.name;
             this.model.catalogEntityData = "LOAD"
@@ -522,7 +531,7 @@ define([
             var that = this
             var tab = $.find('#appClassTab')
             var type = $(tab).find('#app-java-type').val()
-            if (!_.contains(this.catalogApplicationIds, type)) {
+            if (!this.options.catalog.applications.hasType(type)) {
                 $('.entity-info-message').show('slow').delay(2000).hide('slow')
                 return false
             }
@@ -609,13 +618,15 @@ define([
         events:{
             'click #add-selector-container':'addLocation',
             'click #remove-app-location':'removeLocation',
-            'change .select-location': 'selection',
+            'change .select-version': 'selectionVersion',
+            'change .select-location': 'selectionLocation',
             'blur #application-name':'updateName',
             'click #remove-config':'removeConfigRow',
             'click #add-config':'addConfigRow'
         },
 
         template:_.template(DeployHtml),
+        versionOptionTemplate:_.template(DeployVersionOptionHtml),
         locationRowTemplate:_.template(DeployLocationRowHtml),
         locationOptionTemplate:_.template(DeployLocationOptionHtml),
 
@@ -630,11 +641,35 @@ define([
         renderName:function () {
             this.$('#application-name').val(this.model.spec.get("name"))
         },
+        renderVersions: function() {
+            var optionTemplate = this.versionOptionTemplate
+                select = this.$('.select-version')
+                container = this.$('#app-versions')
+                defaultVersion = '0.0.0.SNAPSHOT';
+
+            select.empty();
+
+            var versions = this.options.catalog.applications.getVersions(this.model.spec.get('type'));
+            for (var vi = 0; vi < versions.length; vi++) {
+                var version = versions[vi];
+                select.append(optionTemplate({
+                    version: version
+                }));
+            }
+
+            if (versions.length === 1 && versions[0] === defaultVersion) {
+                this.model.spec.set('version', '');
+                container.hide();
+            } else {
+                this.model.spec.set('version', versions[0]);
+                container.show();
+            }
+        },
         renderAddedLocations:function () {
             // renders the locations added to the model
             var rowTemplate = this.locationRowTemplate,
                 optionTemplate = this.locationOptionTemplate,
-                container = this.$("#selector-container");
+                container = this.$("#selector-container-location");
             container.empty();
             for (var li = 0; li < this.model.spec.get("locations").length; li++) {
                 var chosenLocation = this.model.spec.get("locations")[li];
@@ -675,6 +710,7 @@ define([
             // clear any error message (we are being displayed fresh; if there are errors in the update, we'll show them in code below)
             this.$('div.error-message').hide();
             this.renderName()
+            this.renderVersions()
             this.locations.fetch({
                 success:function () {
                     if (that.model.spec.get("locations").length==0)
@@ -749,7 +785,10 @@ define([
             })
             return map;
         },
-        selection:function (event) {
+        selectionVersion:function (event) {
+            this.model.spec.set("version", $(event.currentTarget).val())
+        },
+        selectionLocation:function (event) {
             var loc_id = $(event.currentTarget).val(),
                 isNoneLocation = loc_id === NO_LOCATION_INDICATOR;
             var locationValid = isNoneLocation || this.locations.find(function (candidate) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/133c3199/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-step-template-entry.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-step-template-entry.html b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-step-template-entry.html
index b72bb10..3234c2d 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-step-template-entry.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/create-step-template-entry.html
@@ -18,7 +18,7 @@ specific language governing permissions and limitations
 under the License.
 -->
 
-<div class="template-lozenge frame" id="<%- id %>" data-name="<%- name %>" data-yaml="<%- planYaml %>">
+<div class="template-lozenge frame" id="<%- id %>" data-type="<%- type %>" data-name="<%- name %>" data-yaml="<%- planYaml %>">
     <% if (iconUrl) { %>
     <div class="icon">
         <img src="<%- iconUrl %>" alt="(icon)" />

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/133c3199/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-version-option.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-version-option.html b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-version-option.html
new file mode 100644
index 0000000..72bed97
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy-version-option.html
@@ -0,0 +1,23 @@
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<option value="<%- version %>">
+    <span class="provider"><%- version %></span>
+</option>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/133c3199/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy.html b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy.html
index 3d79241..d2ea9a9 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/app-add-wizard/deploy.html
@@ -30,9 +30,14 @@ under the License.
         <div class="label-important">ERROR</div>  <span class="error-message-text">Failure performing specified action</span>
     </div>
 
+    <div id="app-versions" class="control-group">
+        <div class="deploy-label">Version</div>
+        <select class="select-version" style="margin:4px 0 4px 0; width:80%"></select>
+    </div>
+
     <div id="app-locations" class="control-group">
         <div class="deploy-label">Locations</div>
-        <div id="selector-container"></div>
+        <div id="selector-container-location"></div>
         <button id="add-selector-container" class="btn btn-info btn-mini">
             Add Additional Location</button>
     </div>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/133c3199/usage/jsgui/src/test/javascript/specs/model/catalog-application-spec.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/test/javascript/specs/model/catalog-application-spec.js b/usage/jsgui/src/test/javascript/specs/model/catalog-application-spec.js
new file mode 100644
index 0000000..df2bb94
--- /dev/null
+++ b/usage/jsgui/src/test/javascript/specs/model/catalog-application-spec.js
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+define([
+    'underscore', 'model/catalog-application'
+], function (_, CatalogApplication) {
+    var catalogApplication = new CatalogApplication.Model
+    catalogApplication.url = 'fixtures/catalog-application.json'
+    catalogApplication.fetch({async:false})
+
+    describe('model/catalog-application', function() {
+        it('loads data from fixture file', function () {
+            expect(catalogApplication.get('id')).toEqual('com.example.app:1.1')
+            expect(catalogApplication.get('type')).toEqual('com.example.app')
+            expect(catalogApplication.get('name')).toEqual('My example application')
+            expect(catalogApplication.get('version')).toEqual('1.1')
+            expect(catalogApplication.get('description')).toEqual('My awesome example application, as a catalog item')
+            expect(catalogApplication.get('planYaml')).toEqual('services:\n- type: brooklyn.entity.basic.VanillaSoftwareProcess\n  launch.command: echo \"Launch application\"\n  checkRunning.command: echo \"Check running application\"')
+            expect(catalogApplication.get('iconUrl')).toEqual('http://my.example.com/icon.png')
+        })
+    })
+    describe("model/catalog-application", function () {
+        it('fetches from /v1/locations', function () {
+            var catalogApplicationCollection = new CatalogApplication.Collection()
+            expect(catalogApplicationCollection.url).toEqual('/v1/catalog/applications')
+        })
+
+        // keep these in describe so jasmine-maven will load them from the file pointed by URL
+        var catalogApplicationFixture = new CatalogApplication.Collection
+        catalogApplicationFixture.url = 'fixtures/catalog-application-list.json'
+        catalogApplicationFixture.fetch()
+
+        it('loads all model properties defined in fixtures/catalog-application.json', function () {
+            expect(catalogApplicationFixture.length).toEqual(3)
+
+            var catalogApplication1 = catalogApplicationFixture.at(0)
+            expect(catalogApplication1.get('id')).toEqual('com.example.app:1.1')
+            expect(catalogApplication1.get('type')).toEqual('com.example.app')
+            expect(catalogApplication1.get('name')).toEqual('My example application')
+            expect(catalogApplication1.get('version')).toEqual('1.1')
+            expect(catalogApplication1.get('description')).toEqual('My awesome example application, as a catalog item')
+            expect(catalogApplication1.get('planYaml')).toEqual('services:\n- type: brooklyn.entity.basic.VanillaSoftwareProcess\n  launch.command: echo \"Launch application\"\n  checkRunning.command: echo \"Check running application\"')
+            expect(catalogApplication1.get('iconUrl')).toEqual('http://my.example.com/icon.png')
+
+            var catalogApplication2 = catalogApplicationFixture.at(1)
+            expect(catalogApplication2.get('id')).toEqual('com.example.app:2.0')
+            expect(catalogApplication2.get('type')).toEqual('com.example.app')
+            expect(catalogApplication2.get('name')).toEqual('My example application')
+            expect(catalogApplication2.get('version')).toEqual('2.0')
+            expect(catalogApplication2.get('description')).toEqual('My awesome example application, as a catalog item')
+            expect(catalogApplication2.get('planYaml')).toEqual('services:\n- type: brooklyn.entity.basic.VanillaSoftwareProcess\n  launch.command: echo \"Launch application\"\n  checkRunning.command: echo \"Check running application\"')
+            expect(catalogApplication2.get('iconUrl')).toEqual('http://my.example.com/icon.png')
+
+            var catalogApplication3 = catalogApplicationFixture.at(2)
+            expect(catalogApplication3.get('id')).toEqual('com.example.other.app:1.0')
+            expect(catalogApplication3.get('type')).toEqual('com.example.other.app')
+            expect(catalogApplication3.get('name')).toEqual('Another example application')
+            expect(catalogApplication3.get('version')).toEqual('1.0')
+            expect(catalogApplication3.get('description')).toEqual('Another awesome example application, as a catalog item')
+            expect(catalogApplication3.get('planYaml')).toEqual('services:\n- type: brooklyn.entity.basic.VanillaSoftwareProcess\n  launch.command: echo \"Launch other application\"\n  checkRunning.command: echo \"Check running other application\"')
+            expect(catalogApplication3.get('iconUrl')).toEqual('http://my.other.example.com/icon.png')
+        })
+
+        it ('Collection#getDistinctApplications returns all available applications, group by type', function() {
+            var groupBy = catalogApplicationFixture.getDistinctApplications()
+
+            expect(Object.keys(groupBy).length).toBe(2)
+            expect(groupBy.hasOwnProperty('com.example.app')).toBeTruthy()
+            expect(groupBy['com.example.app'].length).toBe(2)
+            expect(groupBy['com.example.app'][0].get('version')).toEqual('1.1')
+            expect(groupBy['com.example.app'][1].get('version')).toEqual('2.0')
+            expect(groupBy.hasOwnProperty('com.example.other.app')).toBeTruthy()
+            expect(groupBy['com.example.other.app'].length).toBe(1)
+            expect(groupBy['com.example.other.app'][0].get('version')).toEqual('1.0')
+        })
+
+        it('Collection#getTypes() returns only distinct types', function() {
+            var types = catalogApplicationFixture.getTypes()
+
+            expect(types.length).toBe(2)
+            expect(types[0]).toEqual('com.example.app')
+            expect(types[1]).toEqual('com.example.other.app')
+        })
+
+        describe('Collection#hasType()', function() {
+            it('Returns true if the given type exists within the applications list', function() {
+                var ret = catalogApplicationFixture.hasType('com.example.other.app')
+
+                expect(ret).toBeTruthy()
+            })
+
+            it('Returns false if the given type exists within the applications list', function() {
+                var ret = catalogApplicationFixture.hasType('com.example.other.app.that.does.not.exist')
+
+                expect(ret).toBeFalsy()
+            })
+        })
+
+        describe('Collection#getVersions()', function() {
+            it('Returns an empty array if no applications exist with the given type', function() {
+                var versions = catalogApplicationFixture.getVersions('com.example.other.app.that.does.not.exist')
+
+                expect(versions.length).toBe(0)
+            })
+
+            it('Returns the expected array of versions if applications exist with the given type', function() {
+                var versions = catalogApplicationFixture.getVersions('com.example.app')
+
+                expect(versions.length).toBe(2)
+                expect(versions[0]).toEqual('1.1')
+                expect(versions[1]).toEqual('2.0')
+            })
+        })
+    })
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/133c3199/usage/rest-api/src/test/resources/fixtures/catalog-application-list.json
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/test/resources/fixtures/catalog-application-list.json b/usage/rest-api/src/test/resources/fixtures/catalog-application-list.json
new file mode 100644
index 0000000..68314f0
--- /dev/null
+++ b/usage/rest-api/src/test/resources/fixtures/catalog-application-list.json
@@ -0,0 +1,29 @@
+[
+    {
+        "id": "com.example.app:1.1",
+        "type": "com.example.app",
+        "name": "My example application",
+        "version": "1.1",
+        "description": "My awesome example application, as a catalog item",
+        "planYaml": "services:\n- type: brooklyn.entity.basic.VanillaSoftwareProcess\n  launch.command: echo \"Launch application\"\n  checkRunning.command: echo \"Check running application\"",
+        "iconUrl": "http://my.example.com/icon.png"
+    },
+    {
+        "id": "com.example.app:2.0",
+        "type": "com.example.app",
+        "name": "My example application",
+        "version": "2.0",
+        "description": "My awesome example application, as a catalog item",
+        "planYaml": "services:\n- type: brooklyn.entity.basic.VanillaSoftwareProcess\n  launch.command: echo \"Launch application\"\n  checkRunning.command: echo \"Check running application\"",
+        "iconUrl": "http://my.example.com/icon.png"
+    },
+    {
+        "id": "com.example.other.app:1.0",
+        "type": "com.example.other.app",
+        "name": "Another example application",
+        "version": "1.0",
+        "description": "Another awesome example application, as a catalog item",
+        "planYaml": "services:\n- type: brooklyn.entity.basic.VanillaSoftwareProcess\n  launch.command: echo \"Launch other application\"\n  checkRunning.command: echo \"Check running other application\"",
+        "iconUrl": "http://my.other.example.com/icon.png"
+    }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/133c3199/usage/rest-api/src/test/resources/fixtures/catalog-application.json
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/test/resources/fixtures/catalog-application.json b/usage/rest-api/src/test/resources/fixtures/catalog-application.json
new file mode 100644
index 0000000..2a3ed8d
--- /dev/null
+++ b/usage/rest-api/src/test/resources/fixtures/catalog-application.json
@@ -0,0 +1,9 @@
+{
+    "id": "com.example.app:1.1",
+    "type": "com.example.app",
+    "name": "My example application",
+    "version": "1.1",
+    "description": "My awesome example application, as a catalog item",
+    "planYaml": "services:\n- type: brooklyn.entity.basic.VanillaSoftwareProcess\n  launch.command: echo \"Launch application\"\n  checkRunning.command: echo \"Check running application\"",
+    "iconUrl": "http://my.example.com/icon.png"
+}
\ No newline at end of file


[2/2] incubator-brooklyn git commit: This closes #727

Posted by al...@apache.org.
This closes #727


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/8ad732c8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/8ad732c8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/8ad732c8

Branch: refs/heads/master
Commit: 8ad732c84779316fcf6719ce3d40dbb1489aa42c
Parents: dd0ab7f 133c319
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jul 23 22:31:02 2015 -0700
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Jul 23 22:31:02 2015 -0700

----------------------------------------------------------------------
 usage/jsgui/pom.xml                             |   2 +-
 .../assets/js/model/catalog-application.js      |  55 ++++++++
 .../assets/js/view/application-add-wizard.js    | 107 ++++++++++-----
 .../create-step-template-entry.html             |   2 +-
 .../app-add-wizard/deploy-version-option.html   |  23 ++++
 .../assets/tpl/app-add-wizard/deploy.html       |   7 +-
 .../specs/model/catalog-application-spec.js     | 130 +++++++++++++++++++
 .../fixtures/catalog-application-list.json      |  29 +++++
 .../resources/fixtures/catalog-application.json |   9 ++
 9 files changed, 327 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8ad732c8/usage/jsgui/pom.xml
----------------------------------------------------------------------