Skip to content

Commit bdf13f8

Browse files
Merge pull request ManageIQ#529 from chalettu/templates-create
Added ability to create Orchestration Templates
2 parents 3d34560 + 415df14 commit bdf13f8

23 files changed

+384
-10
lines changed

client/app/app.module.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { CoreModule } from './core/core.module.js';
88
import { DialogsModule } from './dialogs/dialogs.module.js';
99
import { RequestsModule } from './requests/requests.module.js';
1010
import { ServicesModule } from './services/services.module.js';
11+
import { TemplatesModule } from './templates/templates.module.js';
1112

1213
export default angular
1314
.module('app', [
@@ -21,6 +22,7 @@ export default angular
2122
DialogsModule,
2223
RequestsModule,
2324
ServicesModule,
25+
TemplatesModule,
2426
])
2527
.controller('AppController', AppController)
2628
.name;
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { SharedModule } from '../shared/shared.module.js';
2-
import { TemplateExplorerComponent } from './template-explorer/template-explorer.component.js';
32

43
export default angular
54
.module('app.components', [
65
SharedModule,
76
])
8-
.component('templateExplorer', TemplateExplorerComponent)
97
.name;

client/app/core/core.module.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ import { SharedModule } from '../shared/shared.module.js';
4545
import { ShoppingCartComponent } from './shopping-cart/shopping-cart.component.js';
4646
import { ShoppingCartFactory } from './shopping-cart.service.js';
4747
import { SkinModule } from '../skin/skin.module.js';
48-
import { TemplatesServiceFactory } from './templates.service.js';
4948
import { TagEditorFactory } from './tag-editor-modal/tag-editor-modal.service.js';
5049
import { gettextInit } from './gettext.config.js';
5150
import { layoutInit } from './layouts.config.js';
@@ -97,7 +96,6 @@ export const CoreModule = angular
9796
.factory('ServerInfo', ServerInfo)
9897
.factory('Session', SessionFactory)
9998
.factory('ShoppingCart', ShoppingCartFactory)
100-
.factory('TemplatesService', TemplatesServiceFactory)
10199
.factory('TagEditorModal', TagEditorFactory)
102100
.factory('logger', LoggerService)
103101
.factory('TaggingService', TaggingService)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* eslint-disable sort-imports */
2+
import ace from 'brace';
3+
import 'brace/mode/text';
4+
import 'brace/theme/monokai';
5+
6+
export const AceEditorComponent = {
7+
controller: ComponentController,
8+
controllerAs: 'vm',
9+
bindings: {
10+
content: "=",
11+
},
12+
template: '<div class="ace-editor"></div>',
13+
};
14+
15+
/** @ngInject */
16+
function ComponentController() {
17+
var vm = this;
18+
vm.$postLink = function () {
19+
vm.editor = loadAceEditor('text');
20+
vm.editor.$blockScrolling = Infinity;
21+
if (vm.content) {
22+
vm.editor.getSession().setValue(vm.content);
23+
}
24+
vm.editor.getSession().on('change', function (_e) {
25+
vm.content = vm.editor.getValue();
26+
});
27+
};
28+
29+
vm.$onDestroy = function () {
30+
vm.editor.destroy();
31+
};
32+
33+
function loadAceEditor(mode) {
34+
var editor = ace.edit(angular.element.find('.ace-editor')[0]);
35+
editor.session.setMode("ace/mode/" + mode);
36+
editor.renderer.setShowPrintMargin(false);
37+
38+
return editor;
39+
}
40+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import templateUrl from './select-dropdown.html';
2+
3+
export const SelectDropdownComponent = {
4+
controllerAs: 'vm',
5+
bindings: {
6+
model: "=",
7+
options: '<',
8+
required: '<?',
9+
},
10+
templateUrl,
11+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<select pf-select ng-model="vm.model" ng-options="item as item.label for item in vm.options track by item.value" ng-required="vm.required">
2+
</select>

client/app/shared/shared.module.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
formatBytes,
33
megaBytes,
44
} from './format-bytes.filter.js';
5-
5+
import { AceEditorComponent } from './ace-editor/ace-editor.component.js';
66
import { AutofocusDirective } from './autofocus.directive.js';
77
import { ConfirmationDirective } from './confirmation/confirmation.directive.js';
88
import { CustomDropdownComponent } from './custom-dropdown/custom-dropdown.component.js';
@@ -12,6 +12,7 @@ import { LoadingComponent } from './loading.component.js';
1212
import { ModalActionsComponent } from './modal-actions/modal-actions.component.js';
1313
import { PaginationComponent } from './pagination/pagination.component.js';
1414
import { SSCardComponent } from './ss-card/ss-card.component.js';
15+
import { SelectDropdownComponent } from './select-dropdown/select-dropdown.component.js';
1516
import { TaggingComponent } from './tagging/tagging.component.js';
1617
import { substitute } from './substitute.filter.js';
1718

@@ -30,8 +31,10 @@ export const SharedModule = angular
3031
.component('loading', LoadingComponent)
3132
.component('modalActions', ModalActionsComponent)
3233
.component('ssCard', SSCardComponent)
34+
.component('selectDropdown', SelectDropdownComponent)
3335
.component('taggingWidget', TaggingComponent)
3436
.directive('autofocus', AutofocusDirective)
37+
.component('aceEditor', AceEditorComponent)
3538
.directive('confirmation', ConfirmationDirective)
3639
.filter('formatBytes', formatBytes)
3740
.filter('megaBytes', megaBytes)

client/app/states/states.module.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {ServicesDetailsState} from "./services/details/details.state.js";
2626
import {ServicesExplorerState} from "./services/explorer/explorer.state.js";
2727
import {ServicesReconfigureState} from "./services/reconfigure/reconfigure.state.js";
2828
import {ServicesState} from "./services/services.state.js";
29+
import {TemplatesEditorState} from "./templates/editor/editor.state.js";
2930
import {TemplatesExplorerState} from "./templates/explorer/explorer.state.js";
3031
import {TemplatesState} from "./templates/templates.state.js";
3132
import {VmsDetailsState} from "./vms/details/details.state.js";
@@ -65,6 +66,7 @@ export const AppRoutingModule = angular
6566
.run(ServicesExplorerState)
6667
.run(ServicesReconfigureState)
6768
.run(ServicesState)
69+
.run(TemplatesEditorState)
6870
.run(TemplatesExplorerState)
6971
.run(TemplatesState)
7072
.run(VmsDetailsState)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<div class="breadcrumb-bar">
2+
<span>
3+
<ol class="breadcrumb">
4+
<li><a ui-sref="templates">{{'Templates'|translate}}</a></li>
5+
<li class="active"><strong>{{vm.title}}</strong></li>
6+
</ol>
7+
</span>
8+
</div>
9+
<template-editor template="vm.template">
10+
</template-editor>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import templateUrl from './editor.html';
2+
/** @ngInject */
3+
4+
export function TemplatesEditorState(routerHelper) {
5+
routerHelper.configureStates(getStates());
6+
}
7+
8+
function getStates() {
9+
return {
10+
'templates.editor': {
11+
url: '/edit/:templateId',
12+
templateUrl,
13+
controller: StateController,
14+
controllerAs: 'vm',
15+
resolve: {
16+
template: resolveTemplate,
17+
},
18+
},
19+
};
20+
}
21+
22+
/** @ngInject */
23+
function resolveTemplate($stateParams, TemplatesService) {
24+
return $stateParams.templateId ? TemplatesService.getTemplate($stateParams.templateId) : {};
25+
}
26+
27+
/** @ngInject */
28+
function StateController($stateParams, template) {
29+
var vm = this;
30+
if ($stateParams.templateId) {
31+
vm.title = 'Edit Template';
32+
vm.template = template;
33+
} else {
34+
vm.title = 'Add Template';
35+
}
36+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@import 'app_colors'
2+
3+
.ace-editor
4+
5+
border: 1px solid $color-gray-border
6+
height: 300px
7+
width: 100%
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/* eslint camelcase: "off" */
2+
import './_template-editor.sass';
3+
import templateUrl from './template-editor.html';
4+
5+
export const TemplateEditorComponent = {
6+
controller: ComponentController,
7+
controllerAs: 'vm',
8+
templateUrl,
9+
bindings: {
10+
template: "<",
11+
},
12+
};
13+
14+
/** @ngInject */
15+
function ComponentController( $state, TemplatesService, EventNotifications, lodash) {
16+
const vm = this;
17+
18+
vm.$onInit = activate();
19+
20+
function activate() {
21+
const templateTypes = [
22+
{
23+
"label": "Amazon Cloudformation",
24+
"value": "OrchestrationTemplateCfn",
25+
},
26+
{
27+
"label": "Openstack Heat",
28+
"value": "OrchestrationTemplateHot",
29+
},
30+
{
31+
"label": "Microsoft Azure",
32+
"value": "OrchestrationTemplateAzure",
33+
},
34+
{
35+
"label": "VNF",
36+
"value": "OrchestrationTemplateVnfd",
37+
},
38+
];
39+
40+
angular.extend(vm, {
41+
saveTemplate: saveTemplate,
42+
templateTypes: templateTypes,
43+
templateTypeValue: '',
44+
cancelChanges: cancelChanges,
45+
});
46+
if (!vm.template) {
47+
vm.template = {
48+
name: '',
49+
type: '',
50+
description: '',
51+
content: '',
52+
orderable: true,
53+
draft: true,
54+
};
55+
} else {
56+
vm.templateTypeValue = lodash.find(templateTypes, { value: vm.template.type });
57+
}
58+
}
59+
60+
function saveTemplate() {
61+
if (!vm.template.id) {
62+
vm.template.type = vm.templateTypeValue.value;
63+
TemplatesService.createTemplate(vm.template).then(createSuccess, createFailure);
64+
}
65+
function createSuccess(_data) {
66+
$state.go('templates.explorer');
67+
}
68+
function createFailure(_data) {
69+
EventNotifications.error(__("There was an error creating the template"));
70+
}
71+
}
72+
73+
function cancelChanges() {
74+
$state.go('templates');
75+
}
76+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
describe('Component: templateEditor', function () {
2+
beforeEach(function () {
3+
browser.sleep(10000);
4+
browser.get('/templates/edit/');
5+
});
6+
it('should have correct breadcrumb', function () {
7+
var breadcrumb = element.all(by.css('.breadcrumb > .active'));
8+
expect(breadcrumb.get(0).getText()).toBe("Add Template");
9+
});
10+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<div class="ss-details-wrapper">
2+
<div class="panel panel-default ss-details-panel">
3+
<div class="panel-body">
4+
<section>
5+
<div class="col-md-12 ss-details-header">
6+
<div class="row">
7+
<div class="col-lg-7 col-md-6 col-sm-6 col-xs-6">
8+
<h2 translate ng-if="vm.template.id">Edit Template</h2>
9+
<h2 translate ng-if="!vm.template.id">Add Template</h2>
10+
</div>
11+
</div>
12+
</div>
13+
</section>
14+
<section class="ss-form">
15+
<form name="templateForm" class="form-horizontal">
16+
<div class="col-md-12">
17+
<div class="row">
18+
<div class="col-lg-7 col-md-6 col-sm-6 col-xs-6">
19+
<h3 translate>Basic Information</h2>
20+
</div>
21+
</div>
22+
<div class="row">
23+
<div class="col-lg-7 col-md-7 col-sm-7 col-xs-7">
24+
<div class="form-horizontal">
25+
<div class="form-group">
26+
<label class="control-label col-sm-4" translate>Name *</label>
27+
<div class="col-sm-8">
28+
<input id="template-editor-name"
29+
class="form-control"
30+
ng-model="vm.template.name" required/>
31+
</div>
32+
</div>
33+
<div class="form-group">
34+
<label class="control-label col-sm-4">{{'Description' | translate}}</label>
35+
<div class="col-sm-8">
36+
<textarea class="form-control" rows="5" ng-model="vm.template.description" />
37+
</div>
38+
</div>
39+
<div class="form-group test">
40+
<label class="control-label col-sm-4">{{'Template Type' | translate}}</label>
41+
<div class="col-sm-8">
42+
<select-dropdown options="vm.templateTypes" required="true" model="vm.templateTypeValue" id="templateType"></select-dropdown>
43+
</div>
44+
</div>
45+
<div>
46+
<label class="control-label col-sm-4">{{'Draft' | translate}}</label>
47+
<div class="col-sm-8">
48+
<input type="checkbox" ng-model="vm.template.draft" />
49+
</div>
50+
</div>
51+
</div>
52+
</div>
53+
</div>
54+
<div class="row">
55+
<div class="col-lg-7 col-md-6 col-sm-6 col-xs-6">
56+
<h3 translate>Code</h2>
57+
</div>
58+
</div>
59+
<ace-editor content="vm.template.content"></ace-editor>
60+
</div>
61+
</form>
62+
</section>
63+
</div>
64+
</div>
65+
<div>
66+
<div class="panel panel-default ss-details-panel ss-details-panel-bottom">
67+
<div class="panel-body">
68+
<section class="ss-details-section pull-left">
69+
<button class="btn btn-primary" type="button" ng-click="vm.saveTemplate()" ng-disabled="templateForm.$invalid" translate>Save</button>
70+
<button class="btn btn-default" type="button" translate ng-click="vm.cancelChanges()">Cancel</button>
71+
</section>
72+
</div>
73+
</div>
74+
</div>
75+
</div>

0 commit comments

Comments
 (0)