Example #1
0
 def __init__(self, server_config=None, **kwargs):
     self._fields = {
         'int': IntegerField(required=True),
         'int_choices': IntegerField(choices=(1, 2), required=True),
         'int_default': IntegerField(default=5, required=True),
         'many': OneToManyField(SampleEntity, required=True),
         'one': OneToOneField(SampleEntity, required=True),
     }
     super().__init__(server_config, **kwargs)
Example #2
0
 def __init__(self, server_config=None, **kwargs):
     self._fields = {
         'name': StringField(),
         'number': IntegerField(),
         'unique': StringField(unique=True)
     }
     self._meta = {'api_path': 'foo'}
     super(SampleEntity, self).__init__(server_config, **kwargs)
Example #3
0
 def __init__(self, server_config=None, **kwargs):
     self._fields = {
         'ignore_me': IntegerField(),
         'many': OneToManyField(SampleEntity),
         'none': OneToOneField(SampleEntity),
         'one': OneToOneField(SampleEntity),
     }
     self._meta = {'api_path': ''}
     super(TestEntity, self).__init__(server_config, **kwargs)
Example #4
0
class SampleEntity(orm.Entity):
    """Sample entity to be used in the tests"""
    name = StringField()
    value = IntegerField()

    class Meta(object):
        """Non-field attributes for this entity."""
        # (too-few-public-methods) pylint:disable=R0903
        api_path = 'foo'
Example #5
0
    def __init__(self, server_config=None, **kwargs):
        if server_config is None:
            server_config = _get_server_config()
        self._server_config = server_config

        # Subclasses usually define a set of fields and metadata before calling
        # `super`, but that's not always the case.
        if not hasattr(self, '_fields'):
            self._fields = {}
        self._fields.setdefault('id', IntegerField())
        if not hasattr(self, '_meta'):
            self._meta = {}

        # Check that a valid set of field values has been passed in.
        if not set(kwargs.keys()).issubset(self._fields.keys()):
            raise NoSuchFieldError(
                'Valid fields are {0}, but received {1} instead.'.format(
                    self._fields.keys(), kwargs.keys()))

        # Iterate through the values passed in and assign them as instance
        # variable to `self`. Make sure to transform entity IDs into entity
        # objects. (This feature is described in the docstring.)
        for field_name, field_value in kwargs.items():  # e.g. ('admin', True)
            field = self._fields[field_name]  # e.g. A BooleanField object
            if isinstance(field, OneToOneField):
                if field_value is None:
                    setattr(self, field_name, field_value)
                else:
                    setattr(
                        self, field_name,
                        _make_entity_from_id(field.gen_value(), field_value,
                                             self._server_config))
            elif isinstance(field, OneToManyField):
                # `try:; …; except TypeError:; raise BadValueError(…)` better
                # follows the "ask forgiveness" principle. However, a TypeError
                # could be raised for any number of reasons. For example,
                # `field_value` could have a faulty __iter__ implementation.
                if not isinstance(field_value, Iterable):
                    raise BadValueError(
                        'An inappropriate value was assigned to the "{0}" '
                        'field. An iterable of entities and/or entity IDs '
                        'should be assigned, but the following was given: {1}'.
                        format(field_name, field_value))
                setattr(
                    self, field_name,
                    _make_entities_from_ids(field.gen_value(), field_value,
                                            self._server_config))
            else:
                setattr(self, field_name, field_value)
Example #6
0
 def __init__(self, server_config=None, **kwargs):
     self._fields = {'one': IntegerField(), 'two': IntegerField()}
     super(TestEntity, self).__init__(server_config, **kwargs)
Example #7
0
class Entity(object):
    """A logical representation of a Foreman entity.

    This class is rather useless as is, and it is intended to be subclassed.
    Subclasses can specify two useful types of information:

    * fields
    * metadata

    Fields are represented by setting class attributes, and metadata is
    represented by settings attributes on the inner class named ``Meta``.

    """
    # The id() builtin is still available within instance methods, class
    # methods, static methods, inner classes, and so on. However, id() is *not*
    # available at the current level of lexical scoping after this point.
    id = IntegerField()  # pylint:disable=C0103

    def __init__(self, **kwargs):
        fields = self.get_fields()
        for field_name, field_value in kwargs.items():
            if field_name not in fields:
                raise NoSuchFieldError(
                    '{0} is not a valid field. Valid fields are {1}.'.format(
                        field_name, ', '.join(fields.keys())))
            setattr(self, field_name, field_value)

    class Meta(object):  # (too-few-public-methods) pylint:disable=R0903
        """Non-field information about this entity.

        This class is a convenient place to store any non-field information
        about an entity. For example, the ``api_path`` variable is used by
        :meth:`robottelo.orm.Entity.path`.

        """

    def path(self, which=None):
        """Return the path to the current entity.

        Return the path to base entities of this entity's type if:

        * ``which`` is ``'base'``, or
        * ``which`` is ``None`` and instance attribute ``id`` is unset.

        Return the path to this exact entity if instance attribute ``id`` is
        set and:

        * ``which`` is ``'self'``, or
        * ``which`` is ``None``.

        Raise :class:`NoSuchPathError` otherwise.

        Child classes may choose to extend this method, especially if a child
        entity offers more than the two URLs supported by default. If extended,
        then the extending class should check for custom parameters before
        calling ``super``::

            def path(self, which):
                if which == 'custom':
                    return urlparse.urljoin(...)
                super(ChildEntity, self).__init__(which)

        This will allow the extending method to accept a custom parameter
        without accidentally raising a :class:`NoSuchPathError`.

        :param str which: Optional. Valid arguments are 'self' and 'base'.
        :return: A fully qualified URL.
        :rtype: str
        :raises robottelo.orm.NoSuchPathError: If no path can be built.

        """
        # (no-member) pylint:disable=E1101
        # It is OK that member ``self.Meta.api_path`` is not found. Subclasses
        # are required to set that attribute if they wish to use this method.
        #
        # Beware of leading and trailing slashes:
        #
        # urljoin('example.com', 'foo') => 'foo'
        # urljoin('example.com/', 'foo') => 'example.com/foo'
        # urljoin('example.com', '/foo') => '/foo'
        # urljoin('example.com/', '/foo') => '/foo'
        base = urlparse.urljoin(helpers.get_server_url() + '/',
                                self.Meta.api_path)
        if which == 'base' or (which is None and 'id' not in vars(self)):
            return base
        elif (which == 'self' or which is None) and 'id' in vars(self):
            return urlparse.urljoin(base + '/', str(self.id))
        raise NoSuchPathError

    @classmethod
    def get_fields(cls):
        """Find all fields attributes of class ``cls``.

        :param cls: Any object. This method is only especially useful if that
            class has attributes that are subclasses of class ``Field``.
        :return: A dict mapping attribute names to ``Field`` objects.
        :rtype: dict

        """
        # When `dir` is called on "a type or class object, the list contains
        # the names of its attributes, and recursively of the attributes of its
        # bases." In constrast, `vars(cls)` returns only the attributes of
        # `cls` while ignoring all parent classes. Thus, this fails for child
        # classes:
        #
        #     for key, val in vars(cls).items():
        #         if isinstance(val, Field):
        #             attrs[key] = val
        #
        attrs = {}
        for field_name in dir(cls):
            field = getattr(cls, field_name)
            if isinstance(field, Field):
                attrs[field_name] = field
        return attrs