Add basic support for embedded types

For example, given:
```javascript
{
  {
    id: 'container',
    type: 'schema',
    resourceFields: {
      restart: {
        type: 'restartPolicy'
      }
    }
  },

  {
    id: 'restartPolicy',
    type: 'schema',
    resourceFields: {
      name: {
        type: 'string'
      },

      maximumRetryCount: {
        type: 'int',
      }
    }
  }
}
```

the `restart` field will now show up as a textarea that you can put JSON in.
It would be nicer if it looked up the properties of a restartPolicy and allowed
you to enter them directly, but nesting complex types is not a terribly simple
change so this will provide a way to set embedded types in the meantime.
This commit is contained in:
Vincent Fiduccia 2015-01-16 14:59:24 -07:00
parent 613e2d8f2a
commit bb2a818a8f
3 changed files with 106 additions and 94 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
.DS_Store .DS_Store
node_modules node_modules
npm-debug.log
bower_components bower_components
dist dist
tmp tmp

View File

@ -1,6 +1,6 @@
{ {
"name": "api-ui", "name": "api-ui",
"version": "1.0.0", "version": "1.0.1",
"description": "Embedded UI for any service that implements the Rancher API spec", "description": "Embedded UI for any service that implements the Rancher API spec",
"contributors": [ "contributors": [
"Go Daddy Operating Company, LLC. (http://godaddy.com)", "Go Daddy Operating Company, LLC. (http://godaddy.com)",
@ -14,7 +14,7 @@
"url": "https://github.com/rancherio/api-ui.git" "url": "https://github.com/rancherio/api-ui.git"
}, },
"scripts": { "scripts": {
"start": "./scripts/server", "start": "./scripts/serve",
"build": "./scripts/build" "build": "./scripts/build"
}, },
"devDependencies": { "devDependencies": {

View File

@ -472,7 +472,6 @@ HTMLApi.prototype.actionLoad = function(name, obj, body)
// The description of the input and output for this action // The description of the input and output for this action
var actionSchema = (isCollection ? objSchema.collectionActions[name] : objSchema.resourceActions[name]); var actionSchema = (isCollection ? objSchema.collectionActions[name] : objSchema.resourceActions[name]);
// The schema for the input // The schema for the input
var actionInput = {}; var actionInput = {};
if ( actionSchema.input ) if ( actionSchema.input )
@ -1349,117 +1348,129 @@ HTMLApi.prototype._flattenFields = function(mode,schema,data)
HTMLApi.prototype._flattenField = function(mode, name, field, data, depth) HTMLApi.prototype._flattenField = function(mode, name, field, data, depth)
{ {
if ( (mode != 'update') && (mode != 'action') && !(mode == 'create' && field.create) )
{
// This field is not set/changeable
return null;
}
depth = depth || 0; depth = depth || 0;
var type = field._typeList[depth]; var type = field._typeList[depth];
if ( mode == 'update' || (mode == 'create' && field.create) || (mode == 'action') ) var isEmbedded = false;
if ( ['string','password','float','int','date','blob','boolean','enum','reference','array','map'].indexOf(type) === -1 && this.getSchema(type) )
{ {
// The value input's name // Show embedded types as JSON, until proper support for embedded types is added.
var formFieldName = name; type = 'json';
isEmbedded = true;
}
// The key input's name, for maps
var formFieldName2 = null; // The value input's name
var subType; var formFieldName = name;
for ( var i = 0 ; i <= depth; i++ )
// The key input's name, for maps
var formFieldName2 = null;
var subType;
for ( var i = 0 ; i <= depth; i++ )
{
subType = field._typeList[i];
if ( subType == 'array' )
{ {
subType = field._typeList[i]; formFieldName += '[]';
if ( subType == 'array' ) }
{ else if ( subType == 'map' )
formFieldName += '[]'; {
} formFieldName2 = formFieldName+'.key{}';
else if ( subType == 'map' ) formFieldName += '.value{}';
{
formFieldName2 = formFieldName+'.key{}';
formFieldName += '.value{}';
}
if ( subType == 'json' )
{
formFieldName += '.json{}';
}
} }
var row = { if ( subType == 'json' || (isEmbedded && i === depth) )
name: name,
formFieldName: formFieldName,
formFieldName2: formFieldName2,
formFieldNameNull: formFieldName+this._magicNull,
required: field.required || false,
writable: (mode == 'action') || (mode == 'update' && field.update) || (mode != 'update' && field.create),
description: field.description,
placeholder: field.placeholder||"",
enlargeable: (type == 'string' && (!field.maxLength || field.maxLength > 63)),
nullCheck: (field.nullable && !field.options && ['string','data','password','number','int','float','reference'].indexOf(field.type) >= 0 ),
type: type,
field: field,
children: null,
value: ''
};
var displayType = field._typeList[ field._typeList.length - 1];
var parentType = field._typeList[ field._typeList.length - 2];
if ( parentType && parentType == 'reference' )
{ {
var link = null; formFieldName += '.json{}';
if ( field.referenceCollection )
{
link = field.referenceCollection;
}
else
{
var displaySchema = this.getSchema(displayType);
if ( displaySchema )
{
link = displaySchema.links['collection'] || displaySchema.links['self'];
}
}
if ( link )
displayType = '<a tabindex="-1" href="' + link + '" target="_blank">' + displayType + '</a>';
} }
}
for ( var i = field._typeList.length - 2 ; i >= depth ; i-- ) var row = {
{ name: name,
displayType = field._typeList[i] + '[' + displayType + ']'; formFieldName: formFieldName,
} formFieldName2: formFieldName2,
formFieldNameNull: formFieldName+this._magicNull,
required: field.required || false,
writable: (mode == 'action') || (mode == 'update' && field.update) || (mode != 'update' && field.create),
description: field.description,
placeholder: field.placeholder||"",
enlargeable: (type == 'string' && (!field.maxLength || field.maxLength > 63)),
nullCheck: (field.nullable && !field.options && ['string','data','password','number','int','float','reference'].indexOf(field.type) >= 0 ),
type: type,
field: field,
children: null,
value: ''
};
row.displayType = displayType; var displayType = field._typeList[ field._typeList.length - 1];
var parentType = field._typeList[ field._typeList.length - 2];
if ( type == 'map' ) if ( isEmbedded || (parentType && parentType == 'reference') )
{
var link = null;
if ( field.referenceCollection )
{ {
row.children = []; link = field.referenceCollection;
var keys = Object.keys(data||{});
var child;
for ( var i = 0 ; i < keys.length ; i++ )
{
child = this._flattenField(mode, name, field, data[keys[i]], depth+1);
child.value2 = keys[i];
child.parentIsMap = true;
row.children.push(child);
}
}
else if ( type == 'array' )
{
row.children = [];
for ( var i = 0 ; i < (data||[]).length ; i++ )
{
row.children.push( this._flattenField(mode, name, field, data[i], depth+1) );
}
}
else if ( type == 'json' )
{
row.value = JSON.stringify(data);
} }
else else
{ {
row.value = data; var displaySchema = this.getSchema(displayType);
if ( displaySchema )
{
link = displaySchema.links['collection'] || displaySchema.links['self'];
}
} }
return row; if ( link )
{
displayType = '<a tabindex="-1" href="' + link + '" target="_blank">' + displayType + '</a>';
}
} }
return null; for ( var i = field._typeList.length - 2 ; i >= depth ; i-- )
{
displayType = field._typeList[i] + '[' + displayType + ']';
}
row.displayType = displayType;
if ( type == 'map' )
{
row.children = [];
var keys = Object.keys(data||{});
var child;
for ( var i = 0 ; i < keys.length ; i++ )
{
child = this._flattenField(mode, name, field, data[keys[i]], depth+1);
child.value2 = keys[i];
child.parentIsMap = true;
row.children.push(child);
}
}
else if ( type == 'array' )
{
row.children = [];
for ( var i = 0 ; i < (data||[]).length ; i++ )
{
row.children.push( this._flattenField(mode, name, field, data[i], depth+1) );
}
}
else if ( type == 'json' )
{
row.value = JSON.stringify(data);
}
else
{
row.value = data;
}
return row;
} }
@ -1751,7 +1762,7 @@ HTMLApi.prototype.subAdd = function(button, name)
children: [field] children: [field]
} }
var html = Handlebars.templates['field.hbs'](par); var html = Handlebars.partials['field.hbs'](par);
// html = '<div><input type="button" onclick="htmlapi.subRemove(this);" value="-">' + html + '</div>'; // html = '<div><input type="button" onclick="htmlapi.subRemove(this);" value="-">' + html + '</div>';
$(button).before(html); $(button).before(html);