diff --git a/stix2/properties.py b/stix2/properties.py index b638f5f..0b52544 100644 --- a/stix2/properties.py +++ b/stix2/properties.py @@ -1,5 +1,6 @@ import re import uuid +import sys class Property(object): @@ -72,21 +73,66 @@ class Property(object): return value -class List(Property): +class ListProperty(Property): def __init__(self, contained): """ contained should be a type whose constructor creates an object from the value """ - self.contained = contained + if contained == StringProperty: + self.contained = StringProperty().string_type + elif contained == BooleanProperty: + self.contained = bool + else: + self.contained = contained def validate(self, value): - # TODO: ensure iterable - for item in value: - self.contained.validate(item) + try: + list_ = self.clean(value) + except ValueError: + raise + + if len(list_) < 1: + raise ValueError("must not be empty.") + + try: + for item in list_: + self.contained.validate(item) + except ValueError: + raise + except AttributeError: + # type of list has no validate() function (eg. built in Python types) + # TODO Should we raise an error here? + pass + + return list_ def clean(self, value): return [self.contained(x) for x in value] + try: + return [self.contained(x) for x in value] + except TypeError: + raise ValueError("must be an iterable over a type whose constructor creates an object from the value.") + + +class StringProperty(Property): + + def __init__(self): + if sys.version_info[0] == 2: + self.string_type = unicode + else: + self.string_type = str + super(StringProperty, self).__init__() + + def clean(self, value): + return self.string_type(value) + + def validate(self, value): + try: + val = self.clean(value) + except ValueError: + raise + return val class TypeProperty(Property): diff --git a/stix2/test/test_properties.py b/stix2/test/test_properties.py index a210302..22a6b44 100644 --- a/stix2/test/test_properties.py +++ b/stix2/test/test_properties.py @@ -1,7 +1,8 @@ import pytest -from stix2.properties import (Property, BooleanProperty, IDProperty, - ReferenceProperty, TypeProperty) +from stix2.properties import (Property, BooleanProperty, ListProperty, + StringProperty, TypeProperty, IDProperty, + ReferenceProperty) def test_property(): @@ -50,6 +51,22 @@ def test_fixed_property(): assert p.validate(p.default()) +def test_list_property(): + p = ListProperty(StringProperty) + + assert p.validate(['abc', 'xyz']) + with pytest.raises(ValueError): + p.validate([]) + + +def test_string_property(): + prop = StringProperty() + + assert prop.validate('foobar') + assert prop.validate(1) + assert prop.validate([1, 2, 3]) + + def test_type_property(): prop = TypeProperty('my-type')