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:
Michal Gershenzon 2016-05-17 13:35:07 +00:00
parent 2eb1e1e603
commit 594b3e27f9
4 changed files with 101 additions and 2 deletions

View File

@ -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.

View File

@ -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):

View File

@ -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'])

View File

@ -0,0 +1,3 @@
---
features:
- New API for validating ad-hoc actions was added.