diff --git a/test/data/subconfig_unicode1_leadership_requires_follower_value.dict b/test/data/subconfig_unicode1_leadership_requires_follower_value.dict index d452302..bdf806d 100644 --- a/test/data/subconfig_unicode1_leadership_requires_follower_value.dict +++ b/test/data/subconfig_unicode1_leadership_requires_follower_value.dict @@ -1 +1 @@ -{"unicode1_leadership_requires_follower_value.options.unicode1.unicode1": ["test", "pas test"], "unicode1_leadership_requires_follower_value.options.unicode1.unicode2": ["super1", null], "unicode1_leadership_requires_follower_value.options.unicode1.unicode3": ["super1", "cannot access to option \"Unicode follower 2\" because has property \"hidden\" (the value of \"Values 'test' must show 'Unicode follower 2'\" is not \"test\")"]} \ No newline at end of file +{"unicode1_leadership_requires_follower_value.options.unicode1.unicode1": ["test", "pas test"], "unicode1_leadership_requires_follower_value.options.unicode1.unicode2": ["super1", null], "unicode1_leadership_requires_follower_value.options.unicode1.unicode3": ["super1", "ne peut acc\u00e9der \u00e0 l'option \"Unicode follower 2\" a cause de la propri\u00e9t\u00e9 \"hidden\" (la valeur de \"Values 'test' must show 'Unicode follower 2'\" n'est pas \"test\")"]} \ No newline at end of file diff --git a/test/data/subconfig_unicode1_leadership_requires_value.dict b/test/data/subconfig_unicode1_leadership_requires_value.dict index 40e7a84..0bd01a4 100644 --- a/test/data/subconfig_unicode1_leadership_requires_value.dict +++ b/test/data/subconfig_unicode1_leadership_requires_value.dict @@ -1 +1 @@ -{"unicode1_leadership_requires_value.options.unicode.unicode": ["test", "val2"], "unicode1_leadership_requires_value.options.unicode.unicode1": ["super1", "super2"], "unicode1_leadership_requires_value.options.unicode.unicode2": ["pas test", "test"], "unicode1_leadership_requires_value.options.unicode.unicode3": [null, "cannot access to option \"Unicode follower 3\" because has property \"hidden\" (the value of \"Values 'test' must show 'Unicode follower 3'\" is not \"test\")"]} \ No newline at end of file +{"unicode1_leadership_requires_value.options.unicode.unicode": ["test", "val2"], "unicode1_leadership_requires_value.options.unicode.unicode1": ["super1", "super2"], "unicode1_leadership_requires_value.options.unicode.unicode2": ["pas test", "test"], "unicode1_leadership_requires_value.options.unicode.unicode3": [null, "ne peut acc\u00e9der \u00e0 l'option \"Unicode follower 3\" a cause de la propri\u00e9t\u00e9 \"hidden\" (la valeur de \"Values 'test' must show 'Unicode follower 3'\" n'est pas \"test\")"]} \ No newline at end of file diff --git a/test/data/subconfig_unicode1_multi_mandatory.prop b/test/data/subconfig_unicode1_multi_mandatory.prop index 95c9f6b..8d1e0a6 100644 --- a/test/data/subconfig_unicode1_multi_mandatory.prop +++ b/test/data/subconfig_unicode1_multi_mandatory.prop @@ -1 +1 @@ -{"unicode1_multi_mandatory.options.unicode": {"null": ["empty", "mandatory"]}} \ No newline at end of file +{"unicode1_multi_mandatory.options.unicode": {"null": ["mandatory", "empty"]}} \ No newline at end of file diff --git a/test/data/unicode1_leadership_requires_follower_value.dict b/test/data/unicode1_leadership_requires_follower_value.dict index 4404b6f..e62b90b 100644 --- a/test/data/unicode1_leadership_requires_follower_value.dict +++ b/test/data/unicode1_leadership_requires_follower_value.dict @@ -1 +1 @@ -{"options.unicode1.unicode1": ["test", "pas test"], "options.unicode1.unicode2": ["super1", null], "options.unicode1.unicode3": ["super1", "cannot access to option \"Unicode follower 2\" because has property \"hidden\" (the value of \"Values 'test' must show 'Unicode follower 2'\" is not \"test\")"]} \ No newline at end of file +{"options.unicode1.unicode1": ["test", "pas test"], "options.unicode1.unicode2": ["super1", null], "options.unicode1.unicode3": ["super1", "ne peut acc\u00e9der \u00e0 l'option \"Unicode follower 2\" a cause de la propri\u00e9t\u00e9 \"hidden\" (la valeur de \"Values 'test' must show 'Unicode follower 2'\" n'est pas \"test\")"]} \ No newline at end of file diff --git a/test/data/unicode1_leadership_requires_value.dict b/test/data/unicode1_leadership_requires_value.dict index 8bff8fb..2b466e3 100644 --- a/test/data/unicode1_leadership_requires_value.dict +++ b/test/data/unicode1_leadership_requires_value.dict @@ -1 +1 @@ -{"options.unicode.unicode": ["test", "val2"], "options.unicode.unicode1": ["super1", "super2"], "options.unicode.unicode2": ["pas test", "test"], "options.unicode.unicode3": [null, "cannot access to option \"Unicode follower 3\" because has property \"hidden\" (the value of \"Values 'test' must show 'Unicode follower 3'\" is not \"test\")"]} \ No newline at end of file +{"options.unicode.unicode": ["test", "val2"], "options.unicode.unicode1": ["super1", "super2"], "options.unicode.unicode2": ["pas test", "test"], "options.unicode.unicode3": [null, "ne peut acc\u00e9der \u00e0 l'option \"Unicode follower 3\" a cause de la propri\u00e9t\u00e9 \"hidden\" (la valeur de \"Values 'test' must show 'Unicode follower 3'\" n'est pas \"test\")"]} \ No newline at end of file diff --git a/test/data/unicode1_multi_mandatory.prop b/test/data/unicode1_multi_mandatory.prop index 651d55e..4c66872 100644 --- a/test/data/unicode1_multi_mandatory.prop +++ b/test/data/unicode1_multi_mandatory.prop @@ -1 +1 @@ -{"options.unicode": {"null": ["empty", "mandatory"]}} \ No newline at end of file +{"options.unicode": {"null": ["mandatory", "empty"]}} \ No newline at end of file diff --git a/test/test_json.py b/test/test_json.py index e365669..24afaa4 100644 --- a/test/test_json.py +++ b/test/test_json.py @@ -26,7 +26,7 @@ def error_to_str(dico): for key, value in dico.items(): if isinstance(value, list): for idx, val in enumerate(value): - if (isinstance(val, str) and val.startswith('cannot access to')) or isinstance(val, PropertiesOptionError): + if (isinstance(val, str) and (val.startswith('cannot access to') or val.startswith('ne peut accéder'))) or isinstance(val, PropertiesOptionError): dico[key][idx] = "PropertiesOptionError" return dico @@ -188,15 +188,23 @@ def test_prop2(): def test_info(): + debug = False + # debug = True for filename in list_data(): with open(filename, 'r') as fh: json = loads(fh.read()) config = Config(json) with open(filename[:-4] + 'info', 'r') as fh: dico = loads(fh.read()) + if debug: + from pprint import pprint + pprint(json) + print('-------------------') + pprint(dico) + for key, values in dico.items(): for info, value in values.items(): - assert getattr(config.option(key).option, info)() == value, 'error for {} in {}'.format(info, filename) + assert getattr(config.option(key).option, info)() == value, 'error for {} info {} in {}'.format(key, info, filename) def test_mod(): diff --git a/tiramisu_json_api/api.py b/tiramisu_json_api/api.py index 40876be..2a667f0 100644 --- a/tiramisu_json_api/api.py +++ b/tiramisu_json_api/api.py @@ -87,16 +87,23 @@ class TiramisuOptionOption: def ismulti(self) -> bool: return self.schema.get('isMulti', False) + def issubmulti(self) -> bool: + return self.schema.get('isSubMulti', False) + def type(self) -> str: if self.isleadership(): return 'leadership' if self.isoptiondescription(): return 'optiondescription' + if self.issymlinkoption(): + return self.config.get_schema(self.schema['opt_path'])['type'] return self.schema['type'] def properties(self) -> List[str]: model = self.model.get(self._path, {}) - return self.config.get_properties(self.model, self._path, None) + if self.isfollower(): + model = model.get(None, {}) + return self.config.get_properties(model, self._path, None) def requires(self) -> None: # FIXME @@ -255,9 +262,9 @@ class TiramisuOptionValue(_Value): return if type_ == 'choice': if value not in self.schema['enum']: - raise Exception('value {} is not in {}'.format(value, self.schema['enum'])) + raise ValueError('value {} is not in {}'.format(value, self.schema['enum'])) elif not isinstance(value, TYPE[type_]): - raise Exception('value {} is not a valid {} '.format(value, type_)) + raise ValueError('value {} is not a valid {} '.format(value, type_)) def set(self, value): type_ = self.schema['type'] @@ -266,9 +273,17 @@ class TiramisuOptionValue(_Value): if not isinstance(value, list): raise Exception('value must be a list') for val in value: - self._validate(type_, val) + if self.schema.get('isSubMulti', False): + for v in val: + self._validate(type_, v) + else: + self._validate(type_, val) else: - self._validate(type_, value) + if self.schema.get('isSubMulti', False): + for val in value: + self._validate(type_, val) + else: + self._validate(type_, value) self.config.modify_value(self.path, self.index, value, @@ -481,9 +496,18 @@ class ContextValue(_Value): def mandatory(self): for key, value in self.dict().items(): - if self.model.get(key, {}).get('required') and \ - value is None or \ - (self.schema.get('isMulti') and (None in value or '' in value)): + if self.config.isfollower(key): + # FIXME test with index + if self.model.get(key, {}).get(None, {}).get('required'): + if self.config.get_schema(key).get('isSubMulti'): + for val in value: + if not val or None in val or '' in val: + yield key + break + elif None in value or '' in value: + yield key + elif value is None or \ + (self.config.get_schema(key).get('isMulti') and (not value or None in value or '' in value)): yield key @@ -495,7 +519,6 @@ class Config: raise Exception('incompatible tiramisu-json format version (got {}, expected {})'.format(json.get('version', '0.0'), TIRAMISU_JSON_VERSION)) self.model = json['model'] self.form = json['form'] - self.form = {} # support pattern for key, option in json['form'].items(): if key != 'null': @@ -580,7 +603,7 @@ class Config: only_raises=True): props = model.get('properties', [])[:] if model.get('required'): - if self.get_schema(path).get('isMulti', False): + if self.get_schema(path).get('isMulti', False) and not self.isfollower(path): props.append('empty') else: props.append('mandatory') @@ -630,7 +653,7 @@ class Config: index: Optional[int]) -> bool: for property_, needs in {'hidden': True, 'display': False}.items(): if property_ in self.temp.get(path, {}): - value = self.temp[path][property_] + return self.temp[path][property_] else: if self.isfollower(path): if self.model.get(path, {}).get('null', {}).get(property_, None) == needs: @@ -645,36 +668,41 @@ class Config: def get_value(self, path: str, index: int=None) -> Any: + schema = self.get_schema(path) + if schema['type'] == 'symlink': + path = schema['opt_path'] + schema = self.get_schema(path) if index is None: if 'value' in self.temp.get(path, {}): value = self.temp[path]['value'] else: value = self.model.get(path, {}).get('value') - if value is None and self.get_schema(path).get('isMulti', False): + if value is None and schema.get('isMulti', False): value = [] else: index = str(index) if 'delete' in self.temp.get(path, {}): value = None - elif index in self.temp.get(path, {}): - if 'delete' in self.temp[path][index]: - value = None - else: - value = self.temp[path] + elif index in self.temp.get(path, {}) and 'delete' in self.temp[path][index]: + value = None + elif 'value' in self.temp.get(path, {}).get(index, {}): + value = self.temp[path] else: value = self.model.get(path) if self.isfollower(path): if self.is_hidden(path, index): value = PropertiesOptionError(None, None, None, opt_type='option') - elif value is not None and index in value: + elif value is not None and 'value' in value.get(index, {}): value = value[index]['value'] else: - value = self.get_schema(path).get('default') + value = schema.get('default') else: if value is not None and index in value and 'value' in value[index]: value = value[index]['value'] else: - value = self.get_schema(path).get('default') + value = schema.get('default') + if value is None and schema.get('isSubMulti', False): + value = [] return value def get_owner(self,