From d9f94958c26875d62507d62dde00d6bdb7f6f5b8 Mon Sep 17 00:00:00 2001 From: Timur Sufiev Date: Wed, 29 Apr 2015 09:57:12 -0700 Subject: [PATCH] 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 --- extensions/mistral/api.py | 57 ++++-------- extensions/mistral/models.py | 7 ++ .../js/mistral.workbook.controllers.js | 93 ++++++++++++------- extensions/mistral/tables.py | 18 +++- .../mistral/templates/mistral/create.html | 20 ++-- extensions/mistral/test/js/workbookSpec.js | 10 ++ extensions/mistral/urls.py | 8 +- extensions/mistral/views.py | 45 ++++++++- 8 files changed, 173 insertions(+), 85 deletions(-) create mode 100644 extensions/mistral/models.py diff --git a/extensions/mistral/api.py b/extensions/mistral/api.py index 54b404c..4cc1a0e 100644 --- a/extensions/mistral/api.py +++ b/extensions/mistral/api.py @@ -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 diff --git a/extensions/mistral/models.py b/extensions/mistral/models.py new file mode 100644 index 0000000..192d048 --- /dev/null +++ b/extensions/mistral/models.py @@ -0,0 +1,7 @@ +from django.db import models + + +class Workbook(models.Model): + name = models.CharField(max_length=50, unique=True) + yaml = models.TextField() + diff --git a/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js b/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js index 1a72232..6159623 100644 --- a/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js +++ b/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js @@ -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; + }; + + }]) })(); \ No newline at end of file diff --git a/extensions/mistral/tables.py b/extensions/mistral/tables.py index c7fa693..540d376 100644 --- a/extensions/mistral/tables.py +++ b/extensions/mistral/tables.py @@ -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' diff --git a/extensions/mistral/templates/mistral/create.html b/extensions/mistral/templates/mistral/create.html index b114455..430d208 100644 --- a/extensions/mistral/templates/mistral/create.html +++ b/extensions/mistral/templates/mistral/create.html @@ -38,7 +38,8 @@ {% block main %}

Create Workbook

-
+
@@ -54,8 +55,10 @@
- - + +
@@ -78,9 +81,12 @@
-
+
{$ workbook.toYAML() $}
+
+ Here will be a fancy Graph View as soon as we implement it! +
@@ -88,8 +94,10 @@
- - + +
diff --git a/extensions/mistral/test/js/workbookSpec.js b/extensions/mistral/test/js/workbookSpec.js index 60e94b6..504ef25 100644 --- a/extensions/mistral/test/js/workbookSpec.js +++ b/extensions/mistral/test/js/workbookSpec.js @@ -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() { + + }); + }); + }) }); diff --git a/extensions/mistral/urls.py b/extensions/mistral/urls.py index 2436518..56415b0 100644 --- a/extensions/mistral/urls.py +++ b/extensions/mistral/urls.py @@ -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[^/]+))?$', + views.EditWorkbookView.as_view(), name='edit'), + url(r'^commit/(?:/(?P[^/]+))?$', + views.CommitWorkbookView.as_view(), name='commit'), + url(r'^actions/types$', views.ActionTypesView.as_view(), + name='action_types') ) diff --git a/extensions/mistral/views.py b/extensions/mistral/views.py index 2e8f673..d3121cb 100644 --- a/extensions/mistral/views.py +++ b/extensions/mistral/views.py @@ -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 = {