Add stub api calls for Mistral server integration
Store the workbooks being edited inside sqlite database of Horizon django app. Now it's possible to: * create a workbook; * see it in the list of workbooks; * edit it; * delete it. To use the models.py DATABASES variable in openstack_dashboard settings needs to be set at least to sqlite3. Change-Id: I9d4c013470e0fc13ef65484c8f6fae69cdad0a05 Implements: blueprint mistral-server-integration
This commit is contained in:
parent
531dc56c64
commit
d9f94958c2
@ -14,56 +14,37 @@
|
||||
|
||||
from horizon.test import utils as test_utils
|
||||
|
||||
|
||||
_workbooks = []
|
||||
from mistral import models
|
||||
|
||||
|
||||
def find_max_id():
|
||||
max_id = 0
|
||||
for workbook in _workbooks:
|
||||
if max_id < int(workbook.id):
|
||||
max_id = int(workbook.id)
|
||||
|
||||
return max_id
|
||||
|
||||
|
||||
def create_workbook(request, json):
|
||||
name = json['name']
|
||||
for workbook in _workbooks:
|
||||
if name == workbook['name']:
|
||||
raise LookupError('Workbook with that name already exists!')
|
||||
|
||||
obj = test_utils.ObjDictWrapper(id=find_max_id()+1, **json)
|
||||
_workbooks.append(obj)
|
||||
def create_workbook(request, name, yaml):
|
||||
wb = models.Workbook.objects.create(name=name, yaml=yaml)
|
||||
wb.save()
|
||||
return True
|
||||
|
||||
|
||||
def modify_workbook(request, json):
|
||||
id = json['id']
|
||||
for i, workbook in enumerate(_workbooks[:]):
|
||||
if unicode(id) == unicode(workbook.id):
|
||||
_workbooks[i] = test_utils.ObjDictWrapper(**json)
|
||||
return True
|
||||
def modify_workbook(request, id, name, yaml):
|
||||
try:
|
||||
wb = models.Workbook.objects.get(id=id)
|
||||
wb.name = name
|
||||
wb.yaml = yaml
|
||||
wb.save()
|
||||
except models.Workbook.DoesNotExist:
|
||||
return False
|
||||
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def remove_workbook(request, id):
|
||||
for i, workbook in enumerate(_workbooks[:]):
|
||||
if unicode(id) == unicode(workbook.id):
|
||||
del _workbooks[i]
|
||||
return True
|
||||
|
||||
return False
|
||||
models.Workbook.objects.get(id=id).delete()
|
||||
|
||||
|
||||
def list_workbooks(request):
|
||||
return _workbooks
|
||||
return models.Workbook.objects.values('id', 'name')
|
||||
|
||||
|
||||
def get_workbook(request, id):
|
||||
for workbook in _workbooks:
|
||||
if unicode(id) == unicode(workbook.id):
|
||||
return workbook.__dict__
|
||||
|
||||
return None
|
||||
try:
|
||||
return models.Workbook.objects.get(id=id)
|
||||
except models.Workbook.DoesNotExist:
|
||||
return None
|
||||
|
7
extensions/mistral/models.py
Normal file
7
extensions/mistral/models.py
Normal file
@ -0,0 +1,7 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Workbook(models.Model):
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
yaml = models.TextField()
|
||||
|
@ -8,41 +8,70 @@
|
||||
.value('baseActionID', 'action')
|
||||
.value('baseWorkflowID', 'workflow')
|
||||
.controller('workbookCtrl',
|
||||
['$scope', 'mistral.workbook.models', 'baseActionID', 'baseWorkflowID',
|
||||
function($scope, models, baseActionId, baseWorkflowId) {
|
||||
$scope.workbook = models.Workbook.create({name: 'My Workbook'});
|
||||
['$scope', 'mistral.workbook.models', '$http',
|
||||
'baseActionID', 'baseWorkflowID',
|
||||
function($scope, models, $http, baseActionId, baseWorkflowId) {
|
||||
$scope.init = function(id, yaml, commitUrl, discardUrl) {
|
||||
$scope.workbookID = id;
|
||||
$scope.commitUrl = commitUrl;
|
||||
$scope.discardUrl = discardUrl;
|
||||
if ( id !== undefined ) {
|
||||
$scope.workbook = models.Workbook.create(jsyaml.safeLoad(yaml));
|
||||
} else {
|
||||
$scope.workbook = models.Workbook.create({name: 'My Workbook'});
|
||||
}
|
||||
};
|
||||
|
||||
function getNextIDSuffix(container, regexp) {
|
||||
var max = Math.max.apply(Math, container.getIDs().map(function(id) {
|
||||
var match = regexp.exec(id);
|
||||
return match && +match[2];
|
||||
}));
|
||||
return max > 0 ? max + 1 : 1;
|
||||
}
|
||||
|
||||
function getWorkbookNextIDSuffix(base) {
|
||||
var containerName = base + 's',
|
||||
regexp = /(workflow|action)([0-9]+)/,
|
||||
container = $scope.workbook.get(containerName);
|
||||
if ( !container ) {
|
||||
throw 'Base should be either "action" or "workflow"!';
|
||||
function getNextIDSuffix(container, regexp) {
|
||||
var max = Math.max.apply(Math, container.getIDs().map(function(id) {
|
||||
var match = regexp.exec(id);
|
||||
return match && +match[2];
|
||||
}));
|
||||
return max > 0 ? max + 1 : 1;
|
||||
}
|
||||
return getNextIDSuffix(container, regexp);
|
||||
}
|
||||
|
||||
$scope.addAction = function() {
|
||||
var nextSuffix = getWorkbookNextIDSuffix(baseActionId),
|
||||
newID = baseActionId + nextSuffix;
|
||||
$scope.workbook.get('actions').push(
|
||||
{name: 'Action ' + nextSuffix}, {id: newID});
|
||||
};
|
||||
function getWorkbookNextIDSuffix(base) {
|
||||
var containerName = base + 's',
|
||||
regexp = /(workflow|action)([0-9]+)/,
|
||||
container = $scope.workbook.get(containerName);
|
||||
if ( !container ) {
|
||||
throw 'Base should be either "action" or "workflow"!';
|
||||
}
|
||||
return getNextIDSuffix(container, regexp);
|
||||
}
|
||||
|
||||
$scope.addWorkflow = function() {
|
||||
var nextSuffix = getWorkbookNextIDSuffix(baseWorkflowId),
|
||||
newID = baseWorkflowId + nextSuffix;
|
||||
$scope.workbook.get('workflows').push(
|
||||
{name: 'Workflow ' + nextSuffix}, {id: newID});
|
||||
};
|
||||
$scope.addAction = function() {
|
||||
var nextSuffix = getWorkbookNextIDSuffix(baseActionId),
|
||||
newID = baseActionId + nextSuffix;
|
||||
$scope.workbook.get('actions').push(
|
||||
{name: 'Action ' + nextSuffix}, {id: newID});
|
||||
};
|
||||
|
||||
}])
|
||||
$scope.addWorkflow = function() {
|
||||
var nextSuffix = getWorkbookNextIDSuffix(baseWorkflowId),
|
||||
newID = baseWorkflowId + nextSuffix;
|
||||
$scope.workbook.get('workflows').push(
|
||||
{name: 'Workflow ' + nextSuffix}, {id: newID});
|
||||
};
|
||||
|
||||
$scope.commitWorkbook = function() {
|
||||
var data = {
|
||||
name: $scope.workbook.get('name').get(),
|
||||
yaml: $scope.workbook.toYAML()
|
||||
};
|
||||
|
||||
$http({
|
||||
url: $scope.commitUrl,
|
||||
method: 'POST',
|
||||
data: data
|
||||
}).success(function(data, status, headers, config) {
|
||||
document.location = $scope.discardUrl;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.discardWorkbook = function() {
|
||||
document.location = $scope.discardUrl;
|
||||
};
|
||||
|
||||
}])
|
||||
})();
|
@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.template import defaultfilters
|
||||
from horizon import tables
|
||||
@ -22,10 +23,19 @@ from mistral import api
|
||||
class CreateWorkbook(tables.LinkAction):
|
||||
name = 'create'
|
||||
verbose_name = _('Create Workbook')
|
||||
url = 'horizon:project:mistral:create'
|
||||
url = reverse_lazy('horizon:project:mistral:edit', args=())
|
||||
icon = 'plus'
|
||||
|
||||
|
||||
class ModifyWorkbook(tables.LinkAction):
|
||||
name = 'modify'
|
||||
verbose_name = _('Modify Workbook')
|
||||
|
||||
def get_link_url(self, datum):
|
||||
return reverse('horizon:project:mistral:edit',
|
||||
args=(self.table.get_object_id(datum),))
|
||||
|
||||
|
||||
class RemoveWorkbook(tables.DeleteAction):
|
||||
name = 'remove'
|
||||
verbose_name = _('Remove Workbook')
|
||||
@ -37,9 +47,11 @@ class RemoveWorkbook(tables.DeleteAction):
|
||||
|
||||
class WorkbooksTable(tables.DataTable):
|
||||
name = tables.Column('name', verbose_name=_('Workbook Name'))
|
||||
running = tables.Column('running', verbose_name=_('Running'),
|
||||
filters=(defaultfilters.yesno,))
|
||||
|
||||
def get_object_id(self, datum):
|
||||
return datum['id']
|
||||
|
||||
class Meta:
|
||||
table_actions = (CreateWorkbook,)
|
||||
row_actions = (ModifyWorkbook, RemoveWorkbook)
|
||||
name = 'workbooks'
|
||||
|
@ -38,7 +38,8 @@
|
||||
|
||||
{% block main %}
|
||||
<h3>Create Workbook</h3>
|
||||
<div id="create-workbook" class="fluid-container" ng-cloak ng-controller="workbookCtrl">
|
||||
<div id="create-workbook" class="fluid-container" ng-cloak ng-controller="workbookCtrl"
|
||||
ng-init="init({{ id|default:'undefined' }}, '{{ yaml }}', '{{ commit_url }}', '{{ discard_url }}')">
|
||||
<div class="well">
|
||||
<div class="two-panels">
|
||||
<div class="left-panel">
|
||||
@ -54,8 +55,10 @@
|
||||
</div>
|
||||
<div class="right-panel">
|
||||
<div class="btn-group btn-toggle pull-right">
|
||||
<button class="btn btn-sm btn-default">Graph</button>
|
||||
<button class="btn btn-sm btn-primary active">YAML</button>
|
||||
<button ng-click="isGraphMode = true" class="btn btn-sm"
|
||||
ng-class="isGraphMode ? 'active btn-primary' : 'btn-default'">Graph</button>
|
||||
<button ng-click="isGraphMode = false" class="btn btn-sm"
|
||||
ng-class="!isGraphMode ? 'active btn-primary' : 'btn-default'">YAML</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -78,9 +81,12 @@
|
||||
<!-- YAML Panel -->
|
||||
<div class="right-panel">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="panel-body" ng-show="!isGraphMode">
|
||||
<pre>{$ workbook.toYAML() $}</pre>
|
||||
</div>
|
||||
<div class="panel-body" ng-show="isGraphMode">
|
||||
Here will be a fancy Graph View as soon as we implement it!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -88,8 +94,10 @@
|
||||
<div class="two-panels">
|
||||
<div class="full-width">
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-default cancel">Cancel</button>
|
||||
<button class="btn btn-primary">Create</button>
|
||||
<button ng-click="discardWorkbook()" class="btn btn-default cancel">Cancel</button>
|
||||
<button ng-click="commitWorkbook()" class="btn btn-primary">
|
||||
{$ workbookID ? 'Modify' : 'Create' $}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -305,5 +305,15 @@ describe('workbook model logic', function() {
|
||||
|
||||
});
|
||||
|
||||
describe("'Create'/'Modify'/'Cancel' actions", function() {
|
||||
it('edit causes a request to an api and a return to main page', function() {
|
||||
|
||||
});
|
||||
|
||||
it('cancel causes just a return to main page', function() {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
|
@ -19,6 +19,10 @@ from mistral import views
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^create$', views.CreateWorkbookView.as_view(), name='create'),
|
||||
url(r'^actions/types$', views.ActionTypesView.as_view(), name='action_types')
|
||||
url(r'^edit/(?:(?P<workbook_id>[^/]+))?$',
|
||||
views.EditWorkbookView.as_view(), name='edit'),
|
||||
url(r'^commit/(?:/(?P<workbook_id>[^/]+))?$',
|
||||
views.CommitWorkbookView.as_view(), name='commit'),
|
||||
url(r'^actions/types$', views.ActionTypesView.as_view(),
|
||||
name='action_types')
|
||||
)
|
||||
|
@ -14,9 +14,10 @@
|
||||
|
||||
import json
|
||||
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django import http
|
||||
from django.views.generic import View
|
||||
from django.views import generic as generic_views
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
from horizon.views import APIView
|
||||
import yaml
|
||||
@ -26,11 +27,47 @@ from mistral import forms as mistral_forms
|
||||
from mistral import tables as mistral_tables
|
||||
|
||||
|
||||
class CreateWorkbookView(APIView):
|
||||
class EditWorkbookView(APIView):
|
||||
template_name = 'project/mistral/create.html'
|
||||
|
||||
def get_context_data(self, workbook_id=None, **kwargs):
|
||||
commit_ns = 'horizon:project:mistral:commit'
|
||||
if workbook_id is None:
|
||||
commit_url = reverse(commit_ns, args=())
|
||||
else:
|
||||
commit_url = reverse(commit_ns, args=(workbook_id,))
|
||||
context = {
|
||||
'commit_url': commit_url,
|
||||
'discard_url': reverse('horizon:project:mistral:index')
|
||||
}
|
||||
if workbook_id is not None:
|
||||
context['id'] = workbook_id
|
||||
context['yaml'] = api.get_workbook(self.request, workbook_id).yaml
|
||||
return context
|
||||
|
||||
class ActionTypesView(View):
|
||||
|
||||
class CommitWorkbookView(generic_views.View):
|
||||
def post(self, request, workbook_id=None, **kwargs):
|
||||
def read_data():
|
||||
data = json.loads(request.read())
|
||||
return data['name'], data['yaml']
|
||||
|
||||
if workbook_id is None:
|
||||
name, yaml = read_data()
|
||||
api.create_workbook(request, name, yaml)
|
||||
message = "The workbook {0} has been successfully created".format(
|
||||
name)
|
||||
else:
|
||||
name, yaml = read_data()
|
||||
api.modify_workbook(request, workbook_id, name, yaml)
|
||||
message = "The workbook {0} has been successfully modified".format(
|
||||
name)
|
||||
messages.success(request, message)
|
||||
return http.HttpResponseRedirect(
|
||||
reverse_lazy('horizon:project:mistral:index'))
|
||||
|
||||
|
||||
class ActionTypesView(generic_views.View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
key = request.GET.get('key')
|
||||
schema = {
|
||||
|
Loading…
Reference in New Issue
Block a user