Skip to content

Commit bb04575

Browse files
committed
Separate custom jsonschema for action parameters
Keep the standard draft 4 schema separate from the customized schema for action parameters. The standard draft is used by the API models and should not mix with the customization.
1 parent ffe4e44 commit bb04575

File tree

6 files changed

+400
-234
lines changed

6 files changed

+400
-234
lines changed

st2common/st2common/models/api/action.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class RunnerTypeAPI(BaseAPI):
5555
"description": "Input parameters for the action runner.",
5656
"type": "object",
5757
"patternProperties": {
58-
"^\w+$": util_schema.get_draft_schema()
58+
"^\w+$": util_schema.get_action_params_schema()
5959
}
6060
},
6161
"required_parameters": {
@@ -139,7 +139,7 @@ class ActionAPI(BaseAPI):
139139
"description": "Input parameters for the action.",
140140
"type": "object",
141141
"patternProperties": {
142-
"^\w+$": util_schema.get_draft_schema()
142+
"^\w+$": util_schema.get_action_params_schema()
143143
}
144144
},
145145
"required_parameters": {

st2common/st2common/util/jsonify.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@
88

99

1010
__all__ = [
11-
'json_encode',
11+
'json_encode'
1212
]
1313

1414

1515
def json_encode(obj, indent=4):
1616
return json.dumps(obj, cls=GenericJSON, indent=indent)
17+
18+
19+
def load_file(path):
20+
with open(path, 'r') as fd:
21+
return json.load(fd)

st2common/st2common/util/schema.py

Lines changed: 0 additions & 231 deletions
This file was deleted.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import os
2+
3+
import six
4+
import jsonschema
5+
from oslo.config import cfg
6+
7+
from st2common.util import jsonify
8+
9+
10+
# https://github.com/json-schema/json-schema/blob/master/draft-04/schema
11+
# The source material is licensed under the AFL or BSD license.
12+
PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)))
13+
SCHEMA_DRAFT4 = jsonify.load_file('%s/draft4.json' % PATH)
14+
SCHEMA_ACTION_PARAMS = jsonify.load_file('%s/action_params.json' % PATH)
15+
16+
SCHEMA_ANY_TYPE = {
17+
"anyOf": [
18+
{"type": "array"},
19+
{"type": "boolean"},
20+
{"type": "integer"},
21+
{"type": "number"},
22+
{"type": "object"},
23+
{"type": "string"}
24+
]
25+
}
26+
27+
28+
def get_standard_draft_schema():
29+
return SCHEMA_DRAFT4
30+
31+
32+
def get_action_params_schema():
33+
return SCHEMA_ACTION_PARAMS
34+
35+
36+
def extend_with_default(validator_class):
37+
validate_properties = validator_class.VALIDATORS["properties"]
38+
39+
def set_defaults(validator, properties, instance, schema):
40+
for error in validate_properties(
41+
validator, properties, instance, schema,
42+
):
43+
yield error
44+
45+
for property, subschema in six.iteritems(properties):
46+
if "default" in subschema:
47+
instance.setdefault(property, subschema["default"])
48+
49+
return jsonschema.validators.extend(
50+
validator_class, {"properties": set_defaults},
51+
)
52+
53+
54+
def get_validator(assign_property_default=False):
55+
validator = jsonschema.Draft4Validator
56+
return extend_with_default(validator) if assign_property_default else validator
57+
58+
59+
def get_parameter_schema(model):
60+
# Dynamically construct JSON schema from the parameters metadata.
61+
schema = {"$schema": cfg.CONF.schema.draft}
62+
from st2common.util.action_db import get_runnertype_by_name
63+
runner_type = get_runnertype_by_name(model.runner_type['name'])
64+
# Any 'required' runner parameter which is provided in the action is no longer
65+
# considered 'required' by the runner. The action could choose to keep it
66+
# 'required' but will have to explicitly call it out.
67+
runner_required_parameters = [p for p in runner_type.required_parameters
68+
if p not in model.parameters]
69+
required = list(set(runner_required_parameters + model.required_parameters))
70+
normalize = lambda x: {k: v if v else SCHEMA_ANY_TYPE for k, v in six.iteritems(x)}
71+
properties = normalize(runner_type.runner_parameters)
72+
properties.update(normalize(model.parameters))
73+
if properties:
74+
schema['title'] = model.name
75+
if model.description:
76+
schema['description'] = model.description
77+
schema['type'] = 'object'
78+
schema['properties'] = properties
79+
if required:
80+
schema['required'] = required
81+
schema['additionalProperties'] = False
82+
return schema

0 commit comments

Comments
 (0)