Use YAML text instead of JSON in HTTP body
* Use YAML text instead of JSON for workbooks, workflows and actions in HTTP body. * Use hook for validating Content-Type * Changed pecan dependency on pecan>=0.8.0 due to hook bug in pecan v0.6.0 Implements blueprint mistral-yaml-request-body Change-Id: I5a55aa438faeca42385e8e25770bce00da2e93a9
This commit is contained in:
parent
7ddbf2060a
commit
b85c37b1c1
|
@ -14,6 +14,8 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
from wsme import types as wtypes
|
||||
|
||||
|
||||
|
@ -58,6 +60,27 @@ class Resource(wtypes.Base):
|
|||
|
||||
return res + "]"
|
||||
|
||||
def to_string(self):
|
||||
return json.dumps(self.to_dict())
|
||||
|
||||
|
||||
class ResourceList(Resource):
|
||||
"""Resource containing the list of other resources."""
|
||||
|
||||
def to_dict(self):
|
||||
d = {}
|
||||
|
||||
for attr in self._wsme_attributes:
|
||||
attr_val = getattr(self, attr.name)
|
||||
|
||||
if isinstance(attr_val, list):
|
||||
if isinstance(attr_val[0], Resource):
|
||||
d[attr.name] = [v.to_dict() for v in attr_val]
|
||||
elif not isinstance(attr_val, wtypes.UnsetType):
|
||||
d[attr.name] = attr_val
|
||||
|
||||
return d
|
||||
|
||||
|
||||
class Link(Resource):
|
||||
"""Web link."""
|
||||
|
|
|
@ -14,11 +14,14 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import pecan
|
||||
from pecan import hooks
|
||||
from pecan import rest
|
||||
from wsme import types as wtypes
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from mistral.api.controllers import resource
|
||||
from mistral.api.hooks import content_type as ct_hook
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral import exceptions as exc
|
||||
from mistral.openstack.common import log as logging
|
||||
|
@ -62,7 +65,7 @@ class Action(resource.Resource):
|
|||
updated_at='1970-01-01T00:00:00.000000')
|
||||
|
||||
|
||||
class Actions(resource.Resource):
|
||||
class Actions(resource.ResourceList):
|
||||
"""A collection of Actions."""
|
||||
|
||||
actions = [Action]
|
||||
|
@ -72,7 +75,12 @@ class Actions(resource.Resource):
|
|||
return cls(actions=[Action.sample()])
|
||||
|
||||
|
||||
class ActionsController(rest.RestController):
|
||||
class ActionsController(rest.RestController, hooks.HookController):
|
||||
# TODO(nmakhotkin): Have a discussion with pecan/WSME folks in order
|
||||
# to have requests and response of different content types. Then
|
||||
# delete ContentTypeHook.
|
||||
__hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(Action, wtypes.text)
|
||||
def get(self, name):
|
||||
|
@ -83,39 +91,43 @@ class ActionsController(rest.RestController):
|
|||
|
||||
return Action.from_dict(db_model.to_dict())
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(Actions, body=Action)
|
||||
def put(self, action):
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
@pecan.expose(content_type="text/plain")
|
||||
def put(self):
|
||||
"""Update one or more actions.
|
||||
|
||||
NOTE: Field 'definition' is allowed to have definitions
|
||||
NOTE: This text is allowed to have definitions
|
||||
of multiple actions. In this case they all will be updated.
|
||||
"""
|
||||
LOG.debug("Update action(s) [definition=%s]" % action.definition)
|
||||
definition = pecan.request.text
|
||||
LOG.debug("Update action(s) [definition=%s]" % definition)
|
||||
|
||||
db_models = actions.update_actions(action.definition)
|
||||
db_acts = actions.update_actions(definition)
|
||||
models_dicts = [db_act.to_dict() for db_act in db_acts]
|
||||
|
||||
actions_list = [Action.from_dict(db_model.to_dict())
|
||||
for db_model in db_models]
|
||||
action_list = [Action.from_dict(act) for act in models_dicts]
|
||||
|
||||
return Actions(actions=actions_list)
|
||||
return Actions(actions=action_list).to_string()
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(Actions, body=Action, status_code=201)
|
||||
def post(self, action):
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
@pecan.expose(content_type="text/plain")
|
||||
def post(self):
|
||||
"""Create a new action.
|
||||
|
||||
NOTE: Field 'definition' is allowed to have definitions
|
||||
NOTE: This text is allowed to have definitions
|
||||
of multiple actions. In this case they all will be created.
|
||||
"""
|
||||
LOG.debug("Create action(s) [definition=%s]" % action.definition)
|
||||
definition = pecan.request.text
|
||||
pecan.response.status = 201
|
||||
|
||||
db_models = actions.create_actions(action.definition)
|
||||
LOG.debug("Create action(s) [definition=%s]" % definition)
|
||||
|
||||
actions_list = [Action.from_dict(db_model.to_dict())
|
||||
for db_model in db_models]
|
||||
db_acts = actions.create_actions(definition)
|
||||
models_dicts = [db_act.to_dict() for db_act in db_acts]
|
||||
|
||||
return Actions(actions=actions_list)
|
||||
action_list = [Action.from_dict(act) for act in models_dicts]
|
||||
|
||||
return Actions(actions=action_list).to_string()
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
|
||||
|
|
|
@ -14,11 +14,14 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import pecan
|
||||
from pecan import hooks
|
||||
from pecan import rest
|
||||
from wsme import types as wtypes
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from mistral.api.controllers import resource
|
||||
from mistral.api.hooks import content_type as ct_hook
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral.openstack.common import log as logging
|
||||
from mistral.services import workbooks
|
||||
|
@ -65,7 +68,9 @@ class Workbooks(resource.Resource):
|
|||
return cls(workbooks=[Workbook.sample()])
|
||||
|
||||
|
||||
class WorkbooksController(rest.RestController):
|
||||
class WorkbooksController(rest.RestController, hooks.HookController):
|
||||
__hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(Workbook, wtypes.text)
|
||||
def get(self, name):
|
||||
|
@ -76,25 +81,28 @@ class WorkbooksController(rest.RestController):
|
|||
|
||||
return Workbook.from_dict(db_model.to_dict())
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(Workbook, body=Workbook)
|
||||
def put(self, workbook):
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
@pecan.expose(content_type="text/plain")
|
||||
def put(self):
|
||||
"""Update a workbook."""
|
||||
LOG.debug("Update workbook [workbook=%s]" % workbook)
|
||||
definition = pecan.request.text
|
||||
LOG.debug("Update workbook [definition=%s]" % definition)
|
||||
|
||||
db_model = workbooks.update_workbook_v2(workbook.to_dict())
|
||||
wb_db = workbooks.update_workbook_v2({'definition': definition})
|
||||
|
||||
return Workbook.from_dict(db_model.to_dict())
|
||||
return Workbook.from_dict(wb_db.to_dict()).to_string()
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(Workbook, body=Workbook, status_code=201)
|
||||
def post(self, workbook):
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
@pecan.expose(content_type="text/plain")
|
||||
def post(self):
|
||||
"""Create a new workbook."""
|
||||
LOG.debug("Create workbook [workbook=%s]" % workbook)
|
||||
definition = pecan.request.text
|
||||
LOG.debug("Create workbook [definition=%s]" % definition)
|
||||
|
||||
db_model = workbooks.create_workbook_v2(workbook.to_dict())
|
||||
wb_db = workbooks.create_workbook_v2({'definition': definition})
|
||||
pecan.response.status = 201
|
||||
|
||||
return Workbook.from_dict(db_model.to_dict())
|
||||
return Workbook.from_dict(wb_db.to_dict()).to_string()
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
|
||||
|
|
|
@ -14,11 +14,14 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import pecan
|
||||
from pecan import hooks
|
||||
from pecan import rest
|
||||
from wsme import types as wtypes
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from mistral.api.controllers import resource
|
||||
from mistral.api.hooks import content_type as ct_hook
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral.openstack.common import log as logging
|
||||
from mistral.services import workflows
|
||||
|
@ -70,7 +73,7 @@ class Workflow(resource.Resource):
|
|||
return e
|
||||
|
||||
|
||||
class Workflows(resource.Resource):
|
||||
class Workflows(resource.ResourceList):
|
||||
"""A collection of workflows."""
|
||||
|
||||
workflows = [Workflow]
|
||||
|
@ -80,7 +83,12 @@ class Workflows(resource.Resource):
|
|||
return cls(workflows=[Workflow.sample()])
|
||||
|
||||
|
||||
class WorkflowsController(rest.RestController):
|
||||
class WorkflowsController(rest.RestController, hooks.HookController):
|
||||
# TODO(nmakhotkin): Have a discussion with pecan/WSME folks in order
|
||||
# to have requests and response of different content types. Then
|
||||
# delete ContentTypeHook.
|
||||
__hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(Workflow, wtypes.text)
|
||||
def get(self, name):
|
||||
|
@ -91,41 +99,46 @@ class WorkflowsController(rest.RestController):
|
|||
|
||||
return Workflow.from_dict(db_model.to_dict())
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(Workflows, body=Workflow)
|
||||
def put(self, workflow):
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
@pecan.expose(content_type="text/plain")
|
||||
def put(self):
|
||||
"""Update one or more workflows.
|
||||
|
||||
NOTE: Field 'definition' is allowed to have definitions
|
||||
NOTE: The text is allowed to have definitions
|
||||
of multiple workflows. In this case they all will be updated.
|
||||
"""
|
||||
LOG.debug("Update workflow(s) [definition=%s]" % workflow.definition)
|
||||
definition = pecan.request.text
|
||||
|
||||
db_models = workflows.update_workflows(workflow.definition)
|
||||
LOG.debug("Update workflow(s) [definition=%s]" % definition)
|
||||
|
||||
workflows_list = [Workflow.from_dict(db_model.to_dict())
|
||||
for db_model in db_models]
|
||||
db_wfs = workflows.update_workflows(definition)
|
||||
models_dicts = [db_wf.to_dict() for db_wf in db_wfs]
|
||||
|
||||
return Workflows(workflows=workflows_list)
|
||||
workflow_list = [Workflow.from_dict(wf) for wf in models_dicts]
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(Workflows, body=Workflow, status_code=201)
|
||||
def post(self, workflow):
|
||||
return Workflows(workflows=workflow_list).to_string()
|
||||
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
@pecan.expose(content_type="text/plain")
|
||||
def post(self):
|
||||
"""Create a new workflow.
|
||||
|
||||
NOTE: Field 'definition' is allowed to have definitions
|
||||
NOTE: The text is allowed to have definitions
|
||||
of multiple workflows. In this case they all will be created.
|
||||
"""
|
||||
LOG.debug("Create workflow(s) [definition=%s]" % workflow.definition)
|
||||
definition = pecan.request.text
|
||||
pecan.response.status = 201
|
||||
|
||||
db_models = workflows.create_workflows(workflow.definition)
|
||||
LOG.debug("Create workflow(s) [definition=%s]" % definition)
|
||||
|
||||
workflows_list = [Workflow.from_dict(db_model.to_dict())
|
||||
for db_model in db_models]
|
||||
db_wfs = workflows.create_workflows(definition)
|
||||
models_dicts = [db_wf.to_dict() for db_wf in db_wfs]
|
||||
|
||||
return Workflows(workflows=workflows_list)
|
||||
workflow_list = [Workflow.from_dict(wf) for wf in models_dicts]
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
return Workflows(workflows=workflow_list).to_string()
|
||||
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
|
||||
def delete(self, name):
|
||||
"""Delete the named workflow."""
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2014 - Mirantis, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from pecan import hooks
|
||||
|
||||
|
||||
class ContentTypeHook(hooks.PecanHook):
|
||||
def __init__(self, content_type, methods=['GET']):
|
||||
"""Content type hook is needed for changing content type of
|
||||
responses but only for some HTTP methods. This is kind of
|
||||
'hack' but it seems impossible using pecan/WSME to set different
|
||||
content types on request and response.
|
||||
|
||||
:param content_type: Content-Type that response should has.
|
||||
:type content_type: str
|
||||
:param methods: HTTP methods that should have response
|
||||
with given content_type.
|
||||
:type methods: list
|
||||
"""
|
||||
self.content_type = content_type
|
||||
self.methods = methods
|
||||
|
||||
def after(self, state):
|
||||
if state.request.method in self.methods:
|
||||
state.response.content_type = self.content_type
|
|
@ -110,20 +110,22 @@ class TestActionsController(base.FunctionalTest):
|
|||
@mock.patch.object(db_api, "get_action", MOCK_ACTION)
|
||||
@mock.patch.object(db_api, "create_or_update_action", MOCK_UPDATED_ACTION)
|
||||
def test_put(self):
|
||||
resp = self.app.put_json('/v2/actions', UPDATED_ACTION)
|
||||
resp = self.app.put(
|
||||
'/v2/actions',
|
||||
UPDATED_ACTION_DEFINITION,
|
||||
headers={'Content-Type': 'text/plain'}
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_int, 200)
|
||||
|
||||
self.assertDictEqual(
|
||||
{'actions': [UPDATED_ACTION]},
|
||||
resp.json
|
||||
)
|
||||
self.assertEqual({"actions": [UPDATED_ACTION]}, resp.json)
|
||||
|
||||
@mock.patch.object(db_api, "create_or_update_action", MOCK_NOT_FOUND)
|
||||
def test_put_not_found(self):
|
||||
resp = self.app.put_json(
|
||||
resp = self.app.put(
|
||||
'/v2/actions',
|
||||
UPDATED_ACTION,
|
||||
UPDATED_ACTION_DEFINITION,
|
||||
headers={'Content-Type': 'text/plain'},
|
||||
expect_errors=True
|
||||
)
|
||||
|
||||
|
@ -131,27 +133,29 @@ class TestActionsController(base.FunctionalTest):
|
|||
|
||||
@mock.patch.object(db_api, "get_action", MOCK_SYSTEM_ACTION)
|
||||
def test_put_system(self):
|
||||
resp = self.app.put_json(
|
||||
resp = self.app.put(
|
||||
'/v2/actions',
|
||||
SYSTEM_ACTION,
|
||||
SYSTEM_ACTION_DEFINITION,
|
||||
headers={'Content-Type': 'text/plain'},
|
||||
expect_errors=True
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_int, 400)
|
||||
self.assertIn('Attempt to modify a system action: std.echo',
|
||||
resp.json['faultstring'])
|
||||
resp.text)
|
||||
|
||||
@mock.patch.object(db_api, "create_action")
|
||||
def test_post(self, mock_mtd):
|
||||
mock_mtd.return_value = ACTION_DB
|
||||
|
||||
resp = self.app.post_json('/v2/actions', ACTION)
|
||||
resp = self.app.post(
|
||||
'/v2/actions',
|
||||
ACTION_DEFINITION,
|
||||
headers={'Content-Type': 'text/plain'}
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_int, 201)
|
||||
self.assertDictEqual(
|
||||
{'actions': [ACTION]},
|
||||
resp.json
|
||||
)
|
||||
self.assertEqual({"actions": [ACTION]}, resp.json)
|
||||
|
||||
mock_mtd.assert_called_once()
|
||||
|
||||
|
@ -166,8 +170,11 @@ class TestActionsController(base.FunctionalTest):
|
|||
|
||||
@mock.patch.object(db_api, "create_action", MOCK_DUPLICATE)
|
||||
def test_post_dup(self):
|
||||
resp = self.app.post_json(
|
||||
'/v2/actions', ACTION, expect_errors=True
|
||||
resp = self.app.post(
|
||||
'/v2/actions',
|
||||
ACTION_DEFINITION,
|
||||
headers={'Content-Type': 'text/plain'},
|
||||
expect_errors=True
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_int, 409)
|
||||
|
|
|
@ -24,10 +24,14 @@ from mistral import exceptions as exc
|
|||
from mistral.services import workbooks
|
||||
from mistral.tests.unit.api import base
|
||||
|
||||
WORKBOOK_DEF = '---'
|
||||
|
||||
UPDATED_WORKBOOK_DEF = '---\nVersion: 2.0'
|
||||
|
||||
WORKBOOK_DB = models.Workbook(
|
||||
id='123',
|
||||
name='book',
|
||||
definition='---',
|
||||
definition=WORKBOOK_DEF,
|
||||
tags=['deployment', 'demo'],
|
||||
scope="public",
|
||||
created_at=datetime.datetime(1970, 1, 1),
|
||||
|
@ -37,7 +41,7 @@ WORKBOOK_DB = models.Workbook(
|
|||
WORKBOOK = {
|
||||
'id': '123',
|
||||
'name': 'book',
|
||||
'definition': '---',
|
||||
'definition': WORKBOOK_DEF,
|
||||
'tags': ['deployment', 'demo'],
|
||||
'scope': 'public',
|
||||
'created_at': '1970-01-01 00:00:00',
|
||||
|
@ -45,9 +49,9 @@ WORKBOOK = {
|
|||
}
|
||||
|
||||
UPDATED_WORKBOOK_DB = copy.copy(WORKBOOK_DB)
|
||||
UPDATED_WORKBOOK_DB['definition'] = '---\nVersion: 2.0'
|
||||
UPDATED_WORKBOOK_DB['definition'] = UPDATED_WORKBOOK_DEF
|
||||
UPDATED_WORKBOOK = copy.copy(WORKBOOK)
|
||||
UPDATED_WORKBOOK['definition'] = '---\nVersion: 2.0'
|
||||
UPDATED_WORKBOOK['definition'] = UPDATED_WORKBOOK_DEF
|
||||
|
||||
MOCK_WORKBOOK = mock.MagicMock(return_value=WORKBOOK_DB)
|
||||
MOCK_WORKBOOKS = mock.MagicMock(return_value=[WORKBOOK_DB])
|
||||
|
@ -74,29 +78,45 @@ class TestWorkbooksController(base.FunctionalTest):
|
|||
|
||||
@mock.patch.object(workbooks, "update_workbook_v2", MOCK_UPDATED_WORKBOOK)
|
||||
def test_put(self):
|
||||
resp = self.app.put_json('/v2/workbooks', UPDATED_WORKBOOK)
|
||||
resp = self.app.put(
|
||||
'/v2/workbooks',
|
||||
UPDATED_WORKBOOK_DEF,
|
||||
headers={'Content-Type': 'text/plain'}
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_int, 200)
|
||||
self.assertDictEqual(UPDATED_WORKBOOK, resp.json)
|
||||
self.assertEqual(UPDATED_WORKBOOK, resp.json)
|
||||
|
||||
@mock.patch.object(workbooks, "update_workbook_v2", MOCK_NOT_FOUND)
|
||||
def test_put_not_found(self):
|
||||
resp = self.app.put_json('/v2/workbooks', UPDATED_WORKBOOK,
|
||||
expect_errors=True)
|
||||
resp = self.app.put_json(
|
||||
'/v2/workbooks',
|
||||
UPDATED_WORKBOOK_DEF,
|
||||
headers={'Content-Type': 'text/plain'},
|
||||
expect_errors=True
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_int, 404)
|
||||
|
||||
@mock.patch.object(workbooks, "create_workbook_v2", MOCK_WORKBOOK)
|
||||
def test_post(self):
|
||||
resp = self.app.post_json('/v2/workbooks', WORKBOOK)
|
||||
resp = self.app.post(
|
||||
'/v2/workbooks',
|
||||
WORKBOOK_DEF,
|
||||
headers={'Content-Type': 'text/plain'}
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_int, 201)
|
||||
self.assertDictEqual(WORKBOOK, resp.json)
|
||||
self.assertEqual(WORKBOOK, resp.json)
|
||||
|
||||
@mock.patch.object(workbooks, "create_workbook_v2", MOCK_DUPLICATE)
|
||||
def test_post_dup(self):
|
||||
resp = self.app.post_json('/v2/workbooks', WORKBOOK,
|
||||
expect_errors=True)
|
||||
resp = self.app.post(
|
||||
'/v2/workbooks',
|
||||
WORKBOOK_DEF,
|
||||
headers={'Content-Type': 'text/plain'},
|
||||
expect_errors=True
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_int, 409)
|
||||
|
||||
|
|
|
@ -98,22 +98,24 @@ class TestWorkflowsController(base.FunctionalTest):
|
|||
|
||||
@mock.patch.object(db_api, "create_or_update_workflow", MOCK_UPDATED_WF)
|
||||
def test_put(self):
|
||||
resp = self.app.put_json('/v2/workflows', UPDATED_WF)
|
||||
resp = self.app.put(
|
||||
'/v2/workflows',
|
||||
UPDATED_WF_DEFINITION,
|
||||
headers={'Content-Type': 'text/plain'}
|
||||
)
|
||||
|
||||
self.maxDiff = None
|
||||
|
||||
self.assertEqual(resp.status_int, 200)
|
||||
self.assertDictEqual(
|
||||
{'workflows': [UPDATED_WF]},
|
||||
resp.json
|
||||
)
|
||||
self.assertDictEqual({'workflows': [UPDATED_WF]}, resp.json)
|
||||
|
||||
@mock.patch.object(db_api, "create_or_update_workflow", MOCK_NOT_FOUND)
|
||||
def test_put_not_found(self):
|
||||
resp = self.app.put_json(
|
||||
resp = self.app.put(
|
||||
'/v2/workflows',
|
||||
UPDATED_WF,
|
||||
expect_errors=True
|
||||
UPDATED_WF_DEFINITION,
|
||||
headers={'Content-Type': 'text/plain'},
|
||||
expect_errors=True,
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_int, 404)
|
||||
|
@ -122,13 +124,14 @@ class TestWorkflowsController(base.FunctionalTest):
|
|||
def test_post(self, mock_mtd):
|
||||
mock_mtd.return_value = WF_DB
|
||||
|
||||
resp = self.app.post_json('/v2/workflows', WF)
|
||||
resp = self.app.post(
|
||||
'/v2/workflows',
|
||||
WF_DEFINITION,
|
||||
headers={'Content-Type': 'text/plain'}
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_int, 201)
|
||||
self.assertDictEqual(
|
||||
{'workflows': [WF]},
|
||||
resp.json
|
||||
)
|
||||
self.assertDictEqual({'workflows': [WF]}, resp.json)
|
||||
|
||||
mock_mtd.assert_called_once()
|
||||
|
||||
|
@ -139,7 +142,12 @@ class TestWorkflowsController(base.FunctionalTest):
|
|||
|
||||
@mock.patch.object(db_api, "create_workflow", MOCK_DUPLICATE)
|
||||
def test_post_dup(self):
|
||||
resp = self.app.post_json('/v2/workflows', WF, expect_errors=True)
|
||||
resp = self.app.post(
|
||||
'/v2/workflows',
|
||||
WF_DEFINITION,
|
||||
headers={'Content-Type': 'text/plain'},
|
||||
expect_errors=True
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_int, 409)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
pbr>=0.6,!=0.7,<1.0
|
||||
eventlet>=0.15.0
|
||||
PyYAML>=3.1.0
|
||||
pecan>=0.5.0
|
||||
pecan>=0.8.0
|
||||
WSME>=0.6
|
||||
amqplib>=0.6.1 # This is not in global requirements (master branch)
|
||||
argparse
|
||||
|
|
Loading…
Reference in New Issue