Commit c7db2923 authored by Xavier Thompson's avatar Xavier Thompson

slapconfiguration:jsonschema: Add set-default

Add `set-default` options to slapconfiguration:jsonschema.
Accepted values are `all|main|shared|none`.
Default value is `none`.

These allow controlling whether default values of parameters that
where not provided by the user are set from the defaults given in
the JSON schema. The default behavior of the recipe is now not to
add any defaults. The new option allows adding all defaults, only
the defaults of the main instance, or only the defaults of shared
instances.
parent df2bc8bc
...@@ -276,6 +276,16 @@ class Serialised(Recipe): ...@@ -276,6 +276,16 @@ class Serialised(Recipe):
return {} return {}
class BasicValidator(object):
def __init__(self, schema):
self.schema = schema
self.validator = jsonschema.validators.validator_for(schema)(schema)
def validate(self, instance):
for error in self.validator.iter_errors(instance):
yield error
class DefaultValidator(object): class DefaultValidator(object):
def __init__(self, schema): def __init__(self, schema):
self.schema = schema self.schema = schema
...@@ -347,6 +357,20 @@ class JsonSchema(Recipe): ...@@ -347,6 +357,20 @@ class JsonSchema(Recipe):
All instance schemas must be available at the advertised relative paths. All instance schemas must be available at the advertised relative paths.
Example: Example:
${buildout:directory}/software.cfg.json ${buildout:directory}/software.cfg.json
set-default
Enum to control adding defaults specified by the JSON schema
to both/neither/either-of main and shared instance parameters.
Accepted values: all|main|shared|none.
Default value: none.
Example:
shared
Example:
true
set-shared-default
Flag to add defaults specified by the JSON schema for shared instances.
False by default; any value makes this flag behave as true.
Example:
true
""" """
def _schema(self, options): def _schema(self, options):
path = options['jsonschema'] path = options['jsonschema']
...@@ -367,7 +391,7 @@ class JsonSchema(Recipe): ...@@ -367,7 +391,7 @@ class JsonSchema(Recipe):
def _parseParameterDict(self, software_schema, parameter_dict): def _parseParameterDict(self, software_schema, parameter_dict):
instance_schema = software_schema.getInstanceRequestParameterSchema() instance_schema = software_schema.getInstanceRequestParameterSchema()
instance = parameter_dict if isinstance(parameter_dict, dict) else {} instance = parameter_dict if isinstance(parameter_dict, dict) else {}
validator = DefaultValidator(instance_schema) validator = self.Validator(instance_schema)
errors = list(validator.validate(instance)) errors = list(validator.validate(instance))
if errors: if errors:
err = SoftwareReleaseSchemaValidationError(errors).format_error(indent=2) err = SoftwareReleaseSchemaValidationError(errors).format_error(indent=2)
...@@ -380,7 +404,7 @@ class JsonSchema(Recipe): ...@@ -380,7 +404,7 @@ class JsonSchema(Recipe):
if not shared_list: if not shared_list:
return return
shared_schema = self._getSharedSchema(software_schema) shared_schema = self._getSharedSchema(software_schema)
validator = DefaultValidator(shared_schema) validator = self.SharedValidator(shared_schema)
valid, invalid = [], [] valid, invalid = [], []
for instance in shared_list: for instance in shared_list:
reference = instance.pop('slave_reference') reference = instance.pop('slave_reference')
...@@ -397,7 +421,23 @@ class JsonSchema(Recipe): ...@@ -397,7 +421,23 @@ class JsonSchema(Recipe):
options['valid-shared-instance-list'] = valid options['valid-shared-instance-list'] = valid
options['invalid-shared-instance-list'] = invalid options['invalid-shared-instance-list'] = invalid
def _parseOption(self, options, key, default):
value = options.get(key, default)
accepted = ('none', 'main', 'shared', 'all')
try:
index = accepted.index(value)
except ValueError:
raise UserError(
"%r is not a valid value for option %r"
"Accepted values are %r" % (value, key, accepted)
)
# return: value in ('main', 'all'), value in ('shared', 'all')
return index & 1, index & 2
def _expandParameterDict(self, options, parameter_dict): def _expandParameterDict(self, options, parameter_dict):
set_main, set_shared = self._parseOption(options, 'set-default', 'none')
self.Validator = DefaultValidator if set_main else BasicValidator
self.SharedValidator = DefaultValidator if set_shared else BasicValidator
software_schema = self._schema(options) software_schema = self._schema(options)
serialisation = software_schema.getSerialisation(strict=True) serialisation = software_schema.getSerialisation(strict=True)
if serialisation == SoftwareReleaseSerialisation.JsonInXml: if serialisation == SoftwareReleaseSerialisation.JsonInXml:
......
...@@ -183,11 +183,11 @@ class SlapConfigurationTest(unittest.TestCase): ...@@ -183,11 +183,11 @@ class SlapConfigurationTest(unittest.TestCase):
slapconfiguration.JsonSchema(self.buildout, "slapconfiguration", options) slapconfiguration.JsonSchema(self.buildout, "slapconfiguration", options)
return options return options
def receiveParameters(self, options=()): def receiveParameters(self, options=(('set-default', 'all'),)):
options = self.runJsonSchemaRecipe(options) options = self.runJsonSchemaRecipe(options)
return options['configuration'] return options['configuration']
def receiveSharedParameters(self, options=()): def receiveSharedParameters(self, options=(('set-default', 'all'),)):
options = self.runJsonSchemaRecipe(options) options = self.runJsonSchemaRecipe(options)
self.assertNotIn('slave-instance-list', options) self.assertNotIn('slave-instance-list', options)
valid = options['valid-shared-instance-list'] valid = options['valid-shared-instance-list']
...@@ -215,6 +215,27 @@ class SlapConfigurationTest(unittest.TestCase): ...@@ -215,6 +215,27 @@ class SlapConfigurationTest(unittest.TestCase):
received = self.receiveParameters() received = self.receiveParameters()
self.checkParametersWithDefaults(received, parameters) self.checkParametersWithDefaults(received, parameters)
def test_jsonschema_json_in_xml_valid_input_without_defaults(self):
self.writeJsonSchema()
parameters = {"number": 1}
with self.patchSlap(parameters, True):
received = self.receiveParameters(options=())
self.assertEqual(received, parameters)
def test_jsonschema_json_in_xml_valid_input_with_only_shared_defaults(self):
self.writeJsonSchema()
parameters = {"number": 1}
with self.patchSlap(parameters, True):
received = self.receiveParameters({'set-default': 'shared'})
self.assertEqual(received, parameters)
def test_jsonschema_json_in_xml_valid_input_with_only_main_defaults(self):
self.writeJsonSchema()
parameters = {"number": 1}
with self.patchSlap(parameters, True):
received = self.receiveParameters({'set-default': 'main'})
self.checkParametersWithDefaults(received, parameters)
def test_jsonschema_json_in_xml_valid_xml_input_full(self): def test_jsonschema_json_in_xml_valid_xml_input_full(self):
self.writeJsonSchema() self.writeJsonSchema()
parameters = {"letter": "b", "number": 1} parameters = {"letter": "b", "number": 1}
...@@ -270,7 +291,7 @@ class SlapConfigurationTest(unittest.TestCase): ...@@ -270,7 +291,7 @@ class SlapConfigurationTest(unittest.TestCase):
parameters = {"number": 1} parameters = {"number": 1}
shared = [{"kind": 1}] shared = [{"kind": 1}]
with self.patchSlap(parameters, True, shared): with self.patchSlap(parameters, True, shared):
valid, invalid = self.receiveParameters(shared=True) valid, invalid = self.receiveSharedParameters()
self.assertEqual(invalid, {}) self.assertEqual(invalid, {})
self.assertEqual(list(valid.values()), [{"kind": 1, "thing": "hello"}]) self.assertEqual(list(valid.values()), [{"kind": 1, "thing": "hello"}])
...@@ -302,6 +323,30 @@ class SlapConfigurationTest(unittest.TestCase): ...@@ -302,6 +323,30 @@ class SlapConfigurationTest(unittest.TestCase):
invalid_values = list(invalid.values()) invalid_values = list(invalid.values())
self.assertEqual(invalid_values, [{"kind": 2, "thing": "forty-two"}]) self.assertEqual(invalid_values, [{"kind": 2, "thing": "forty-two"}])
def test_jsonschema_shared_2_valid_without_defaults(self):
self.writeJsonSchema()
parameters = {"number": 1}
shared = [{"kind": 2}]
with self.patchSlap(parameters, True, shared):
valid, _ = self.receiveSharedParameters(options=())
self.assertEqual(list(valid.values()), shared)
def test_jsonschema_shared_2_valid_with_only_main_defaults(self):
self.writeJsonSchema()
parameters = {"number": 1}
shared = [{"kind": 2}]
with self.patchSlap(parameters, True, shared):
valid, _ = self.receiveSharedParameters({'set-default': 'main'})
self.assertEqual(list(valid.values()), shared)
def test_jsonschema_shared_2_valid_with_only_shared_defaults(self):
self.writeJsonSchema()
parameters = {"number": 1}
shared = [{"kind": 2}]
with self.patchSlap(parameters, True, shared):
valid, _ = self.receiveSharedParameters({'set-default': 'shared'})
self.assertEqual(list(valid.values()), [{"kind": 2, "thing": 42}])
def test_jsonschema_shared_1_and_2_valid_defaults(self): def test_jsonschema_shared_1_and_2_valid_defaults(self):
self.writeJsonSchema() self.writeJsonSchema()
parameters = {"number": 1} parameters = {"number": 1}
...@@ -344,6 +389,6 @@ class SlapConfigurationTest(unittest.TestCase): ...@@ -344,6 +389,6 @@ class SlapConfigurationTest(unittest.TestCase):
parameters = {"number": 1} parameters = {"number": 1}
shared = [{"kind": 1}, {"kind": 2, "thing": "hello"}] shared = [{"kind": 1}, {"kind": 2, "thing": "hello"}]
with self.patchSlap(parameters, True, shared): with self.patchSlap(parameters, True, shared):
valid, invalid = self.receiveParameters(shared=True) valid, invalid = self.receiveSharedParameters()
self.assertEqual(list(valid.values()), [{"kind": 1, "thing": "hello"}]) self.assertEqual(list(valid.values()), [{"kind": 1, "thing": "hello"}])
self.assertEqual(list(invalid.values()), [{"kind": 2, "thing": "hello"}]) self.assertEqual(list(invalid.values()), [{"kind": 2, "thing": "hello"}])
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment