Add API to validate ad-hoc action
Add an API that gets an ad-hoc action DSL and validates it. This is done in the same way workflows are validated today. Change-Id: Ibbb949ef38befae1ef83a2a56cda4c817ceb41d4 Implements: blueprint validate-ad-hoc-action-api
This commit is contained in:
parent
2eb1e1e603
commit
594b3e27f9
@ -190,7 +190,7 @@ There are three service groups according to Mistral architecture currently, name
|
|||||||
Validation
|
Validation
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Validation endpoints allow to check correctness of workbook and workflow DSL without having to upload them into Mistral.
|
Validation endpoints allow to check correctness of workbook, workflow and ad-hoc action DSL without having to upload them into Mistral.
|
||||||
|
|
||||||
**POST /v2/workbooks/validation**
|
**POST /v2/workbooks/validation**
|
||||||
Validate workbook content (DSL grammar and semantics).
|
Validate workbook content (DSL grammar and semantics).
|
||||||
@ -198,4 +198,7 @@ Validation endpoints allow to check correctness of workbook and workflow DSL wit
|
|||||||
**POST /v2/workflows/validation**
|
**POST /v2/workflows/validation**
|
||||||
Validate workflow content (DSL grammar and semantics).
|
Validate workflow content (DSL grammar and semantics).
|
||||||
|
|
||||||
These endpoints expect workbook or workflow text (DSL) correspondingly in a request body.
|
**POST /v2/actions/validation**
|
||||||
|
Validate ad-hoc action content (DSL grammar and semantics).
|
||||||
|
|
||||||
|
These endpoints expect workbook, workflow or ad-hoc action text (DSL) correspondingly in a request body.
|
||||||
|
@ -22,11 +22,13 @@ import wsmeext.pecan as wsme_pecan
|
|||||||
|
|
||||||
from mistral.api.controllers import resource
|
from mistral.api.controllers import resource
|
||||||
from mistral.api.controllers.v2 import types
|
from mistral.api.controllers.v2 import types
|
||||||
|
from mistral.api.controllers.v2 import validation
|
||||||
from mistral.api.hooks import content_type as ct_hook
|
from mistral.api.hooks import content_type as ct_hook
|
||||||
from mistral.db.v2 import api as db_api
|
from mistral.db.v2 import api as db_api
|
||||||
from mistral import exceptions as exc
|
from mistral import exceptions as exc
|
||||||
from mistral.services import actions
|
from mistral.services import actions
|
||||||
from mistral.utils import rest_utils
|
from mistral.utils import rest_utils
|
||||||
|
from mistral.workbook import parser as spec_parser
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
SCOPE_TYPES = wtypes.Enum(str, 'private', 'public')
|
SCOPE_TYPES = wtypes.Enum(str, 'private', 'public')
|
||||||
@ -93,6 +95,9 @@ class ActionsController(rest.RestController, hooks.HookController):
|
|||||||
# delete ContentTypeHook.
|
# delete ContentTypeHook.
|
||||||
__hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]
|
__hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]
|
||||||
|
|
||||||
|
validate = validation.SpecValidationController(
|
||||||
|
spec_parser.get_action_list_spec_from_yaml)
|
||||||
|
|
||||||
@rest_utils.wrap_wsme_controller_exception
|
@rest_utils.wrap_wsme_controller_exception
|
||||||
@wsme_pecan.wsexpose(Action, wtypes.text)
|
@wsme_pecan.wsexpose(Action, wtypes.text)
|
||||||
def get(self, name):
|
def get(self, name):
|
||||||
|
@ -36,6 +36,35 @@ my_action:
|
|||||||
output: "{$.str1}{$.str2}"
|
output: "{$.str1}{$.str2}"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
ACTION_DEFINITION_INVALID_NO_BASE = """
|
||||||
|
---
|
||||||
|
version: '2.0'
|
||||||
|
|
||||||
|
my_action:
|
||||||
|
description: My super cool action.
|
||||||
|
tags: ['test', 'v2']
|
||||||
|
|
||||||
|
base-input:
|
||||||
|
output: "{$.str1}{$.str2}"
|
||||||
|
"""
|
||||||
|
|
||||||
|
ACTION_DEFINITION_INVALID_YAQL = """
|
||||||
|
---
|
||||||
|
version: '2.0'
|
||||||
|
|
||||||
|
my_action:
|
||||||
|
description: My super cool action.
|
||||||
|
tags: ['test', 'v2']
|
||||||
|
base: std.echo
|
||||||
|
base-input:
|
||||||
|
output: <% $. %>
|
||||||
|
"""
|
||||||
|
|
||||||
|
ACTION_DSL_PARSE_EXCEPTION = """
|
||||||
|
---
|
||||||
|
%
|
||||||
|
"""
|
||||||
|
|
||||||
SYSTEM_ACTION_DEFINITION = """
|
SYSTEM_ACTION_DEFINITION = """
|
||||||
---
|
---
|
||||||
version: '2.0'
|
version: '2.0'
|
||||||
@ -339,3 +368,62 @@ class TestActionsController(base.APITest):
|
|||||||
self.assertEqual(400, resp.status_int)
|
self.assertEqual(400, resp.status_int)
|
||||||
|
|
||||||
self.assertIn("Unknown sort direction", resp.body.decode())
|
self.assertIn("Unknown sort direction", resp.body.decode())
|
||||||
|
|
||||||
|
def test_validate(self):
|
||||||
|
resp = self.app.post(
|
||||||
|
'/v2/actions/validate',
|
||||||
|
ACTION_DEFINITION,
|
||||||
|
headers={'Content-Type': 'text/plain'}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(200, resp.status_int)
|
||||||
|
self.assertTrue(resp.json['valid'])
|
||||||
|
|
||||||
|
def test_validate_invalid_model_exception(self):
|
||||||
|
resp = self.app.post(
|
||||||
|
'/v2/actions/validate',
|
||||||
|
ACTION_DEFINITION_INVALID_NO_BASE,
|
||||||
|
headers={'Content-Type': 'text/plain'},
|
||||||
|
expect_errors=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(200, resp.status_int)
|
||||||
|
self.assertFalse(resp.json['valid'])
|
||||||
|
self.assertIn("Invalid DSL", resp.json['error'])
|
||||||
|
|
||||||
|
def test_validate_dsl_parse_exception(self):
|
||||||
|
resp = self.app.post(
|
||||||
|
'/v2/actions/validate',
|
||||||
|
ACTION_DSL_PARSE_EXCEPTION,
|
||||||
|
headers={'Content-Type': 'text/plain'},
|
||||||
|
expect_errors=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(200, resp.status_int)
|
||||||
|
self.assertFalse(resp.json['valid'])
|
||||||
|
self.assertIn("Definition could not be parsed", resp.json['error'])
|
||||||
|
|
||||||
|
def test_validate_yaql_parse_exception(self):
|
||||||
|
resp = self.app.post(
|
||||||
|
'/v2/actions/validate',
|
||||||
|
ACTION_DEFINITION_INVALID_YAQL,
|
||||||
|
headers={'Content-Type': 'text/plain'},
|
||||||
|
expect_errors=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(200, resp.status_int)
|
||||||
|
self.assertFalse(resp.json['valid'])
|
||||||
|
self.assertIn("unexpected end of statement",
|
||||||
|
resp.json['error'])
|
||||||
|
|
||||||
|
def test_validate_empty(self):
|
||||||
|
resp = self.app.post(
|
||||||
|
'/v2/actions/validate',
|
||||||
|
'',
|
||||||
|
headers={'Content-Type': 'text/plain'},
|
||||||
|
expect_errors=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(200, resp.status_int)
|
||||||
|
self.assertFalse(resp.json['valid'])
|
||||||
|
self.assertIn("Invalid DSL", resp.json['error'])
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- New API for validating ad-hoc actions was added.
|
Loading…
Reference in New Issue
Block a user