From b9e6ccb3eae763f88e8dc800cbe25bd7f748769c Mon Sep 17 00:00:00 2001 From: Timur Sufiev Date: Fri, 24 Jul 2015 13:00:18 +0300 Subject: [PATCH] Initial import of data schema from Rackspace HotBuilder Change-Id: I9375ded596250634a139a11291ef2be1190904d6 --- .eslintignore | 3 +- .../static/hotbuilder/js/hotbuilder.models.js | 1125 ++++++++++++++++- 2 files changed, 1123 insertions(+), 5 deletions(-) diff --git a/.eslintignore b/.eslintignore index b36fc47..395e244 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ merlin/static/merlin/libs -merlin/static/merlin/js/custom-libs \ No newline at end of file +merlin/static/merlin/js/custom-libs +extensions/hotbuilder/static/hotbuilder/js/hotbuilder.models.js \ No newline at end of file diff --git a/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.models.js b/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.models.js index 249ea83..2e04bc8 100644 --- a/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.models.js +++ b/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.models.js @@ -1,10 +1,1127 @@ -/** - * Created by tsufiev on 2/24/15. - */ +// Copyright 2014 Rackspace +// +// Licensed 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. (function() { 'use strict'; angular - .module('hotbuilder'); + .module('hotbuilder') + .factory('ModelsService', ModelsService); + + ModelsService.$inject = ['merlin.field.models', 'merlin.utils']; + + function ModelsService(fields, utils) { + var hot = {}; + + var elementSchema, + rpElementSchema, + primitiveExtension = { + create: function (json, parameters) { + var self = Barricade.Primitive.create.call(this, json, + parameters); + hot.Primitive.call(self); + return self; + } + }; + + hot.Primitive = Barricade.Blueprint.create(function () {}); + + hot.Primitive._schema = {}; // QUICK HACK + + hot.Primitive.Factory = function (json, parameters) { + var type = Barricade.getType(json); + + if (json === undefined || type === String) { + return hot.Primitive.string.create(json, parameters); + } else if (type === Boolean) { + return hot.Primitive.bool.create(json, parameters); + } else if (type === Number) { + return hot.Primitive.number.create(json, parameters); + } else if (type === Array) { + return hot.Primitive.array.create(json, parameters); + } else if (type === Object) { + return hot.Primitive.object.create(json, parameters); + } + }; + + elementSchema = { + '@class': hot.Primitive, + '@factory': hot.Primitive.Factory + }; + + hot.Primitive.string = Barricade.Primitive.extend(primitiveExtension, + {'@type': String}); + hot.Primitive.bool = Barricade.Primitive.extend(primitiveExtension, + {'@type': Boolean}); + hot.Primitive.number = Barricade.Primitive.extend(primitiveExtension, + {'@type': Number}); + hot.Primitive.array = Barricade.Array.extend({ + create: function (json, parameters) { + var self = Barricade.Array.create.call(this, json, parameters); + hot.Primitive.call(self); + return self; + } + }, { + '@type': Array, + '*': elementSchema + }); + + hot.Primitive.object = Barricade.MutableObject.extend({ + create: function (json, parameters) { + var self = Barricade.MutableObject + .create.call(this, json, parameters); + hot.Primitive.call(self); + return self; + } + }, { + '@type': Object, + '?': elementSchema + }); + + hot.ResourcePropertyWrapper = Barricade.Base.extend({ + create: function (json, parameters) { + var self = this.extend({}), + value = hot.IntrinsicFunctionFactory(json, parameters); + + if (!parameters) { + parameters = {}; + } + + Barricade.Observable.call(self); + Barricade.Identifiable.call(self, parameters.id); + + if (value) { + self.setValue(value); + } else { + self.setValue(json, parameters); + } + + return self; + }, + getValue: function () { + return this._value; + }, + setValue: function (newValue, newParameters) { + var self = this; + + function onChange() { self.emit('change', this); } + function onChildChange() { self.emit('childChange', this); } + function onReplace(newVal) { self.setValue(newVal); } + + if (this._value) { + this._value.emit('removeFrom', this); + } + + if (this._safeInstanceof(newValue, hot.IntrinsicFunction) || + this._safeInstanceof(newValue, this._innerClass)) { + this._value = newValue; + } else { + this._value = this._innerClass.create(newValue, + newParameters); + } + + this._value.on('change', onChange); + this._value.on('childChange', onChildChange); + this._value.on('replace', onReplace); + + this._value.on('removeFrom', function (container) { + self._value.off('change', onChange); + self._value.off('childChange', onChildChange); + self._value.off('replace', onReplace); + }); + + this.emit('change', 'set', this._value); + }, + toNormal: function () { + this.setValue(); + }, + toIntrinsicFunction: function () { + this.setValue(hot.GetParameter.create()); + }, + hasDescription: function () { + return ('_description' in this) && !this._description.isEmpty(); + }, + getDescription: function () { + return this._description.get(); + }, + hasDependency: function () { + return this._value.hasDependency(); + }, + resolveWith: function (obj) { + return this.getValue().resolveWith(obj); + }, + isEmpty: function () { + return !!this._value; + }, + isUsed: function () { + return this.isRequired() || this._value.isUsed(); + }, + setIsUsed: function (isUsed) { + return this._value.setIsUsed(isUsed); + }, + getIntrinsicFunctions: function () { + function searchValue(value) { + var intrinsics = []; + if (value.instanceof(hot.ResourcePropertyWrapper)) { + return value.getIntrinsicFunctions(); + } else if (value.instanceof(hot.IntrinsicFunction)) { + if (value.instanceof(hot.StringReplace)) { + return [value].concat(value.getIntrinsicFunctions()); + } + return [value]; + } else if (value.instanceof(Barricade.Container)) { + value.each(function (i, element) { + intrinsics = intrinsics.concat(searchValue(element)); + }); + return intrinsics; + } + return []; + } + return searchValue(this.getValue()); + }, + toJSON: function (options) { + return this._value.toJSON(options); + } + }); + + // Extension for Resource properties that have schemaless containers + hot.Primitive.FactoryForResourceProperty = function (json, parameters) { + var type = Barricade.getType(json); + + function getInnerClass() { + if (json === undefined || type === String) { + return hot.Primitive.string; + } else if (type === Boolean) { + return hot.Primitive.bool; + } else if (type === Number) { + return hot.Primitive.number; + } else if (type === Array) { + return hot.Primitive.arrayOfRP; + } else if (type === Object) { + return hot.Primitive.objectOfRP; + } + } + + return hot.ResourcePropertyWrapper.extend({ + _innerClass: getInnerClass() + }, {}).create(json, parameters); + }; + + rpElementSchema = { + '@class': hot.ResourcePropertyWrapper, + '@factory': hot.Primitive.FactoryForResourceProperty + }; + + hot.Primitive.arrayOfRP = hot.Primitive.array.extend({}, { + '*': rpElementSchema + }); + + hot.Primitive.objectOfRP = hot.Primitive.object.extend({}, { + '?': rpElementSchema + }); + + function constraintFactory(constraintObj) { + var types = { + 'range': function (constraint) { + var min = constraint.min, + max = constraint.max; + + function getMessage() { + if (min === undefined) { + return 'Value must be less than ' + max; + } else if (max === undefined) { + return 'Value must be greater than ' + min; + } + return 'Value must be between ' + + min + ' and ' + max; + } + + return function (val) { + return ((min === undefined || val >= min) && + (max === undefined || val <= max)) || + getMessage(); + }; + }, + 'length': function (constraint) { + var min = constraint.min, + max = constraint.max; + + function getMessage() { + if (min === undefined) { + return 'Must have fewer than ' + + max + ' letters'; + } else if (max === undefined) { + return 'Must have more than ' + + min + ' letters'; + } + return 'Must have between ' + + min + ' and ' + max + ' letters'; + } + + return function (val) { + return ((min === undefined || val.length >= min) && + (max === undefined || val.length <= max)) || + getMessage(); + }; + }, + 'allowed_values': function (constraint) { + return function (val) { + return (constraint.indexOf(val) > -1) || + ('Value must be one of ' + constraint.join(', ')); + }; + }, + 'allowed_pattern': function (constraint) { + var regex = new RegExp(constraint); + return function (val) { + return regex.test(val) || + ('Must match pattern: ' + constraint); + }; + }, + 'custom_constraint': function () { + return function () { return true; }; + } + }; + + for (var t in types) { + if (constraintObj.hasOwnProperty(t)) { + return types[t](constraintObj[t]); + } + } + + console.log('Constraint type not found: ', constraintObj); + return function () { return true; }; + } + + hot.ResourcePropertyFactory = function (json, parameters) { + if (!json.hasOwnProperty('schema')) { + if (json.type === 'list') { + return hot.ResourceProperty_list2.create(json, parameters); + } else if (json.type === 'map') { + return hot.ResourceProperty_map2.create(json, parameters); + } + } + return hot['ResourceProperty_' + json.type].create(json, parameters); + }; + + hot.ResourceProperty = Barricade.ImmutableObject.extend({ + getSchema: function () { + var schema = {}, + wrapperSchema = {}, + types = { + 'map': Object, + 'list': Array, + 'string': String, + 'number': Number, + 'integer': Number, + 'boolean': Boolean + }; + + wrapperSchema['@required'] = this.get('required').get(); + schema['@type'] = types[this.get('type').get()]; + schema['@required'] = this.get('required').get(); + schema['@constraints'] = + this.get('constraints').get().map(constraintFactory); + + if (schema['@type'] === Object) { + if (this.get('schema')) { + this.get('schema').each(function (i, prop) { + schema[prop.getID()] = prop.getSchema(); + }); + } else { + schema['?'] = rpElementSchema; + } + } else if (schema['@type'] === Array) { + if (this.get('schema')) { + schema['*'] = this.get('schema').get('*').getSchema(); + } else { + schema['*'] = rpElementSchema; + } + } + + return { + '@class': hot.ResourcePropertyWrapper.extend({ + _innerClass: Barricade.create(schema), + _description: this.get('description') + }, wrapperSchema) + }; + } + }, { + '@type': Object, + 'type': {'@type': String}, + 'description': {'@type': String}, + 'required': {'@type': Boolean}, + 'constraints': {'@type': Array}, + 'update_allowed': {'@type': Boolean} + }); + + hot.ResourceProperty_map = hot.ResourceProperty.extend({}, { + 'default': {'@type': Object}, + 'schema': { + '@type': Object, + '?': { + '@class': hot.ResourceProperty, + '@factory': hot.ResourcePropertyFactory + } + } + }); + + hot.ResourceProperty_list = hot.ResourceProperty.extend({}, { + 'default': {'@type': Array}, + 'schema': { + '@type': Object, + '*': { + '@class': hot.ResourceProperty, + '@factory': hot.ResourcePropertyFactory + } + } + }); + + hot.ResourceProperty_list2 = hot.ResourceProperty.extend({}, { + 'default': {'@type': Array}}); + hot.ResourceProperty_map2 = hot.ResourceProperty.extend({}, { + 'default': {'@type': Object}}); + hot.ResourceProperty_string = hot.ResourceProperty.extend({}, { + 'default': {'@type': String}}); + hot.ResourceProperty_number = hot.ResourceProperty.extend({}, { + 'default': {'@type': Number}}); + hot.ResourceProperty_integer = hot.ResourceProperty.extend({}, { + 'default': {'@type': Number}}); + hot.ResourceProperty_boolean = hot.ResourceProperty.extend({}, { + 'default': {'@type': Boolean}}); + + hot.ResourceAttribute = Barricade.create({ + '@type': Object, + 'description': {'@type': String} + }); + + hot.ResourceType = Barricade.ImmutableObject.extend({ + getSchema: function () { + var propertySchema = { + '@type': Object, + '@required': false + }; + + this.get('properties').each(function (i, self) { + propertySchema[self.getID()] = self.getSchema(); + }); + + if (this.getID() === 'OS::Heat::ResourceGroup') { + propertySchema.resource_def = { + '@type': Object, + 'type': { + '@class': hot.ResourcePropertyWrapper.extend({ + _innerClass: Barricade.create({'@type': String}) + }, {'@required': true}) + }, + 'properties': { + '@type': Object, + '@ref': { + to: hot.ResourceProperties, + needs: function () { + return hot.ResourceProperties; + }, + getter: function (data) { + var type = data.needed.get('resource_def') + .get('type').getValue(); + if (type.instanceof(hot.GetParameter)) { + type = type.get('get_param'); + } + return type; + }, + processor: function (data) { + var type = data.val; + if (type.instanceof(hot.Parameter)) { + type = type.get('default'); + } + return hot.ResourcePropertiesFactory( + data.standIn.get(), undefined, type.get()); + } + } + } + }; + } + + return propertySchema; + } + }, { + '@type': Object, + 'attributes': { + '@type': Object, + '?': {'@class': hot.ResourceAttribute} + }, + 'properties': { + '@type': Object, + '?': { + '@class': hot.ResourceProperty, + '@factory': hot.ResourcePropertyFactory + } + } + }); + + hot.ResourceTypes = Barricade.create({ + '@type': Object, + '?': {'@class': hot.ResourceType} + }); + + hot.IntrinsicFunctionFactory = function (json, parameters) { + if (Barricade.getType(json) === Object) { + if (json.hasOwnProperty('get_resource')) { + return hot.GetResource.create(json, parameters); + + } else if (json.hasOwnProperty('get_attr')) { + return hot.GetAttribute.create(json, parameters); + + } else if (json.hasOwnProperty('get_param')) { + if (json.get_param === 'OS::stack_name' || + json.get_param === 'OS::stack_id' || + json.get_param === 'OS::project_id') { + return hot.GetParameterSpecial.create(json, parameters); + } + return hot.GetParameter.create(json, parameters); + + } else if (json.hasOwnProperty('get_file')) { + return hot.GetFile.create(json, parameters); + + } else if (json.hasOwnProperty('str_replace')) { + return hot.StringReplace.create(json, parameters); + + } else if (json.hasOwnProperty('resource_facade')) { + return hot.ResourceFacade.create(json, parameters); + } + } + + return false; + }; + + hot.IntrinsicFunction = Barricade.ImmutableObject.extend({}, { + '@type': Object, + '@required': false + }); + + hot.GetResource = hot.IntrinsicFunction.extend({}, { + '@toJSON': function () { + if (this.get('get_resource').getPrimitiveType() === String) { + console.error('Resource was not available'); + return {get_resource: ''}; + } else { + return {get_resource: this.get('get_resource').getID()}; + } + }, + 'get_resource': { + '@type': String, + '@ref': { + to: function () { return hot.Resource; }, + needs: function () { return hot.Template; }, + getter: function (data) { + return data.needed.get('resources') + .getByID(data.standIn.get()); + } + } + } + }); + + hot.GetAttribute = hot.IntrinsicFunction.extend({}, { + 'get_attr': { + // handle a nicely-formatted object instead of + // an array with different types in it, the first + // two of which should really be references + '@type': Object, + '@inputMassager': function (json) { + if (!json) { + json = ['', '']; + } + + return { + resource: json[0], + attribute: json[1], + value: json.slice(2) + }; + }, + '@toJSON': function () { + var resourceID; + + if (this.get('resource').getPrimitiveType() === String) { + resourceID = ''; + console.error('Resource was not available'); + } else { + resourceID = this.get('resource').getID(); + } + return [ + resourceID, + this.get('attribute').get() + ].concat(this.get('value').get()); + }, + + 'resource': { + '@type': String, + '@ref': { + to: function () { return hot.Resource; }, + needs: function () { return hot.Template; }, + getter: function (data) { + return data.needed.get('resources') + .getByID(data.standIn.get()); + } + } + }, + 'attribute': {'@type': String}, + 'value': {'@type': Array} + } + }); + + hot.GetParameter = hot.IntrinsicFunction.extend({}, { + '@toJSON': function () { + if (this.get('get_param').getPrimitiveType() === String) { + console.error('Parameter was not available'); + return {get_param: ''}; + } else { + return {get_param: this.get('get_param').getID()}; + } + }, + 'get_param': { + '@type': String, + '@ref': { + to: function () { return hot.Parameter; }, + needs: function () { return hot.Template; }, + getter: function (data) { + return data.needed.get('parameters') + .getByID(data.standIn.get()); + } + } + } + }); + + // For OS::stack_name, OS::stack_id, and OS::project_id pseudo parameters + hot.GetParameterSpecial = hot.IntrinsicFunction.extend({}, { + 'get_param': {'@type': String} + }); + + hot.GetFile = hot.IntrinsicFunction.extend({}, { + 'get_file': {'@type': String} + }); + + hot.StringReplace = hot.IntrinsicFunction.extend({ + getParams: function () { + return this.get('str_replace').get('params'); + }, + getTemplate: function () { + return this.get('str_replace').get('template'); + }, + getIntrinsicFunctions: function () { + return this.getParams().toArray().reduce(function (arr, current) { + return arr.concat(current.getIntrinsicFunctions()); + }, []); + } + }, { + 'str_replace': { + '@type': Object, + 'template': {'@type': String}, + 'params': { + '@type': Object, + '?': { + '@class': hot.ResourcePropertyWrapper.extend({ + _innerClass: Barricade.create({'@type': String}) + }, {}) + } + } + } + }); + + hot.ResourceFacade = hot.IntrinsicFunction.extend({}, { + 'resource_facade': {'@type': String} + }); + + hot.ResourceProperties = Barricade.ImmutableObject.extend({ + canConnectTo: function (typeName) { + return typeName in this._automaticConnections; + }, + getConnector: function (typeName) { + return this._automaticConnections[typeName]; + } + }); + + hot.ResourcePropertiesFactory = function (json, parameters, type) { + var propertyClass = hot.ResourceProperties.Normal[type] || + hot.ResourceProperties.Custom[type] || + hot.ResourceProperties.Null, + propertiesOut; + + return propertyClass.create(json, parameters); + }; + + hot.Resource = Barricade.ImmutableObject.extend({ + _endsWith: function (str, suffix) { + return str.indexOf(suffix, str.length - suffix.length) !== -1; + }, + _getTypeValue: function (typeObj) { + return typeObj.getValue().instanceof(hot.GetParameter) + ? typeObj.getValue().get('get_param').get('default').get() + : typeObj.getValue().get(); + }, + getProperties: function () { + return this.get('properties'); + }, + getProperty: function (property) { + return this.getProperties().get(property); + }, + isResourceGroup: function () { + return this._getTypeValue(this.get('type')) === + 'OS::Heat::ResourceGroup'; + }, + isProviderResource: function () { + var type = this.getType(); + return this._endsWith(type, '.yaml') || + this._endsWith(type, '.template'); + }, + getType: function () { + return this._getTypeValue(this.isResourceGroup() + ? this.getProperty('resource_def').get('type') + : this.get('type')); + }, + getShortType: function () { + var type = this.getType(); + if (this.isProviderResource()) { + return type.split('/').pop().split('.')[0]; + } + return type.split('::')[2]; + }, + getAttributes: function () { + var attributes = this.getProperties().getBackingType() + .get('attributes').toArray(); + if (this.isResourceGroup()) { + return attributes.concat( + this.getProperty('resource_def').get('properties') + .getBackingType().get('attributes').toArray()); + } + return attributes; + }, + getAttributeNames: function () { + return this.getAttributes().map(function (attribute) { + return attribute.getID(); + }); + }, + getIntrinsicFunctions: function () { + function getIntrinsics(value) { + var intrinsics = []; + if (value.instanceof(hot.ResourcePropertyWrapper)) { + return value.getIntrinsicFunctions(); + } else if (value.instanceof(Barricade.Container)) { + value.each(function (i, element) { + intrinsics = intrinsics.concat(getIntrinsics(element)); + }); + return intrinsics; + } + return []; + } + return getIntrinsics(this.getProperties()); + }, + canConnectTo: function (resource, checkBothWays) { + return this.getProperties().canConnectTo(resource.getType()) || + (checkBothWays !== false && resource.canConnectTo(this, false)); + }, + connectTo: function (res, tryBothWays) { + var connector = this.getProperties().getConnector(res.getType()); + if (connector) { + connector(this, res); + } else if (tryBothWays !== false) { + res.connectTo(this, false); + } + }, + _docsBaseURL: "http://docs.rs-heat.com/raxdox/", + getDocsLink: function () { + var resourceType = this.getType(), + nameTokens = resourceType.split('::'), + resourceBase; + if (nameTokens[0] === 'OS') { + resourceBase = "openstack"; + } else if (nameTokens[0] === 'Rackspace') { + resourceBase = "rackspace"; + } else { + console.warn("Resource type not found: %s", nameTokens[0]); + return this._docsBaseURL; + } + return this._docsBaseURL + resourceBase + '.html#' + + resourceType; + } + }, { + '@type': Object, + + 'type': { + '@class': hot.ResourcePropertyWrapper.extend({ + _innerClass: Barricade.create({'@type': String}) + }, {'@required': true}) + }, + 'properties': { + '@type': Object, + '@ref': { + to: hot.ResourceProperties, + needs: function () { return hot.Resource; }, + getter: function (data) { + var type = data.needed.get('type').getValue(); + if (type.instanceof(hot.GetParameter)) { + type = type.get('get_param'); + } + return type; + }, + processor: function (data) { + var type = data.val; + + if (type.instanceof(hot.Parameter)) { + type = type.get('default'); + } + + return hot.ResourcePropertiesFactory( + data.standIn.get(), undefined, type.get()); + } + } + }, + 'metadata': { + '@type': String, + '@required': false + }, + 'depends_on': { + '@type': Array, + '@required': false, + '@inputMassager': function (json) { + if (typeof json === "string") { + return [json]; + } else { + return json; + } + }, + '@toJSON': function (options) { + if (this.toArray().length === 1) { + return this.get(0).toJSON(options); + } + return Barricade.Array.toJSON.call(this, options); + }, + '*': {'@type': String} + }, + 'update_policy': { + '@type': Object, + '@required': false, + '?': {'@type': String} + }, + 'deletion_policy': { + '@type': String, + '@required': false + } + }); + + + hot.ParameterDefault = Barricade.Primitive.extend({}, { + '@required': false + }); + + (function () { + var pd = hot.ParameterDefault; + hot.StringParameterDefault = pd.extend({}, {'@type': String}); + hot.NumberParameterDefault = pd.extend({}, {'@type': Number}); + hot.ArrayParameterDefault = pd.extend({}, {'@type': Array}); + hot.ObjectParameterDefault = pd.extend({}, {'@type': Object}); + hot.BooleanParameterDefault = pd.extend({}, {'@type': Boolean}); + }()); + + hot.ParameterConstraintFactory = function (json, parameters) { + var key, + types = { + 'length': hot.LengthParameterConstraint, + 'range': hot.RangeParameterConstraint, + 'allowed_values': hot.AllowedValuesParameterConstraint, + 'allowed_pattern': hot.AllowedPatternParameterConstraint, + 'custom_constraint': hot.CustomParameterConstraint, + }; + + if (!json) { + return hot.LengthParameterConstraint.create(json, parameters); + } + + for (key in types) { + if (types.hasOwnProperty(key) && json.hasOwnProperty(key)) { + return types[key].create(json, parameters); + } + } + + console.error('unknown constraint type'); + }; + + hot.ParameterConstraint = Barricade.ImmutableObject.extend({}, { + '@type': Object, + 'description': { + '@type': String, + '@required': false + } + }); + + hot.LengthParameterConstraint = hot.ParameterConstraint.extend({}, { + 'length': { + '@type': Object, + 'min': { + '@type': Number, + '@required': false + }, + 'max': { + '@type': Number, + '@required': false + } + } + }); + + hot.RangeParameterConstraint = hot.ParameterConstraint.extend({}, { + 'range': { + '@type': Object, + 'min': { + '@type': Number, + '@required': false + }, + 'max': { + '@type': Number, + '@required': false + } + } + }); + + hot.AllowedValuesParameterConstraint = hot.ParameterConstraint.extend({}, { + 'allowed_values': {'@type': Array}}); + hot.AllowedPatternParameterConstraint = hot.ParameterConstraint.extend({}, { + 'allowed_pattern': {'@type': String}}); + hot.CustomParameterConstraint = hot.ParameterConstraint.extend({}, { + 'custom_constraint': {'@type': String}}); + + hot.Parameter = Barricade.create({ + '@type': Object, + + 'type': {'@type': String}, + 'label': { + '@type': String, + '@required': false + }, + 'description': { + '@type': String, + '@required': false + }, + 'default': { + '@type': Object, + '@required': false, + // default can be various types, so wrap it in an object to + // make Barricade happy + '@inputMassager': function (json) { + return {value: json}; + }, + '@ref': { + to: hot.ParameterDefault, + needs: function () { return hot.Parameter; }, + getter: function (data) { + return data.needed.get('type'); + }, + processor: function (data) { + var types = { + 'string': hot.StringParameterDefault, + '': hot.StringParameterDefault, + 'number': hot.NumberParameterDefault, + 'comma_delimited_list': + hot.ArrayParameterDefault, + 'json': hot.ObjectParameterDefault, + 'boolean': hot.BooleanParameterDefault, + }, + type = data.val.get(); + + return types[type].create(data.standIn.get().value); + } + } + }, + 'hidden': { + '@type': Boolean, + '@required': false, + }, + 'constraints': { + '@type': Array, + '@required': false, + '*': { + '@class': hot.ParameterConstraint, + '@factory': hot.ParameterConstraintFactory + } + } + }); + + hot.ParameterGroup = Barricade.create({ + '@type': Object, + 'label': {'@type': String}, + 'description': { + '@type': String, + '@required': false + }, + 'parameters': { + '@type': Array, + '@toJSON': function () { + return this.toArray().map(function (param) { + return param.getID(); + }); + }, + '*': { + '@type': String, + '@ref': { + to: hot.Parameter, + needs: function () { return hot.Template; }, + getter: function (data) { + return data.needed.get('parameters') + .getByID(data.standIn.get()); + } + } + } + }, + }); + + hot.Output = Barricade.create({ + '@type': Object, + 'description': {'@type': String}, + 'value': { + '@class': hot.ResourcePropertyWrapper.extend({ + _innerClass: Barricade.create({'@type': String}) + }, {}) + } + }); + + hot.Template = Barricade.create({ + '@type': Object, + + 'heat_template_version': {'@type': String}, + 'description': { + '@type': String, + '@required': false + }, + 'parameter_groups': { + '@type': Array, + '@required': false, + '*': {'@class': hot.ParameterGroup} + }, + 'parameters': { + '@type': Object, + '@required': false, + '?': {'@class': hot.Parameter} + }, + 'resources': { + '@type': Object, + '?': {'@class': hot.Resource} + }, + 'outputs': { + '@type': Object, + '@required': false, + '?': {'@class': hot.Output} + } + }); + + //hot.ProviderTemplate = Barricade.ImmutableObject.extend({ + // getSchema: function () { + // function getActualType(type) { + // var types = { + // json: 'map', + // comma_delimited_list: 'list' + // }; + // return types[type] || type; + // } + // + // return { + // properties: this.get('parameters').toArray().reduce( + // function (objOut, param) { + // objOut[param.getID()] = { + // description: param.get('description').get(), + // type: getActualType(param.get('type').get()) + // }; + // return objOut; + // }, {}), + // attributes: this.get('outputs').toArray().reduce( + // function (objOut, output) { + // objOut[output.getID()] = { + // description: output.get('description').get() + // }; + // return objOut; + // }, {}) + // }; + // } + //}, { + // '@type': Object, + // + // 'heat_template_version': {'@type': String}, + // 'description': {'@type': String, '@required': false}, + // 'parameter_groups': {'@type': Array, '@required': false}, + // 'resources': {'@type': Object}, + // 'parameters': { + // '@type': Object, + // '?': {'@class': hot.Parameter} + // }, + // 'outputs': { + // '@type': Object, + // '@required': false, + // '?': { + // '@class': hot.Output.extend({}, { + // 'value': { + // '@class': hot.Primitive, + // '@factory': hot.Primitive.Factory + // } + // }) + // } + // } + //}); + + //// Used to find URLs of templates to load and turn into child templates + //hot.ProviderTemplateHelper = hot.Template.extend({ + // getProviderTemplateURLs: function () { + // return this.get('resources').toArray().filter(function (res) { + // return res.isProviderResource(); + // }).map(function (res) { + // return res.getType(); + // }); + // } + //}, { + // // Essentially, remove any unncessary reference resolving + // '@type': Object, + // 'parameter_groups': {'@type': Array, '@required': false}, + // 'resources': { + // '@type': Object, + // '?': { + // '@class': hot.Resource.extend({}, { + // 'properties': {'@type': Object, '@required': false}, + // 'depends_on': { + // '@type': Array, + // '@required': false, + // '@inputMassager': function (json) { + // return json instanceof Array ? json : [json]; + // } + // } + // }) + // } + // }, + // 'outputs': {'@type': Object, '@required': false} + //}); + + hot.createResourceClass = function (resourceType) { + return hot.ResourceProperties.extend({ + getBackingType: function () { + return resourceType; + }, + _automaticConnections: + HOTUI_RESOURCE_CONNECTIONS[resourceType.getID()] || {} + }, + resourceType.getSchema()); + }; + + //hot.createProviderResourceClass = function (providerTemplateJSON) { + // var pTemplate = hot.ProviderTemplate.create(providerTemplateJSON), + // resType = hot.ResourceType.create(pTemplate.getSchema()); + // return hot.createResourceClass(resType); + //}; + + return hot; + } })();