Skip to content

Commit

Permalink
Added Parameter entity and Host.all_parameters field (#446)
Browse files Browse the repository at this point in the history
* Added Parameter entity

* Added extra test for better coverage

* fix pylint

* Added EntityDeleteMixin and EntityUpdateMixin for Parameter
  • Loading branch information
abalakh authored and ldjebran committed Oct 6, 2017
1 parent 7091bfa commit 2d306b4
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 0 deletions.
61 changes: 61 additions & 0 deletions nailgun/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -2946,6 +2946,7 @@ class Host( # pylint:disable=too-many-instance-attributes

def __init__(self, server_config=None, **kwargs):
self._fields = {
'all_parameters': entity_fields.ListField(),
'architecture': entity_fields.OneToOneField(Architecture),
'build': entity_fields.BooleanField(),
'capabilities': entity_fields.StringField(),
Expand Down Expand Up @@ -4303,6 +4304,66 @@ def read(self, entity=None, attrs=None, ignore=None, params=None):
return super(OverrideValue, self).read(entity, attrs, ignore, params)


class Parameter(
Entity,
EntityCreateMixin,
EntityDeleteMixin,
EntityReadMixin,
EntityUpdateMixin):
"""A representation of a Parameter entity."""

def __init__(self, server_config=None, **kwargs):
self._fields = {
'name': entity_fields.StringField(
required=True,
str_type='alpha',
length=(6, 12),
),
'priority': entity_fields.IntegerField(),
'value': entity_fields.StringField(required=True),
}
self._path_fields = {
'domain': entity_fields.OneToOneField(Domain),
'host': entity_fields.OneToOneField(Host),
'hostgroup': entity_fields.OneToOneField(HostGroup),
'location': entity_fields.OneToOneField(Location),
'operatingsystem': entity_fields.OneToOneField(OperatingSystem),
'organization': entity_fields.OneToOneField(Organization),
'subnet': entity_fields.OneToOneField(Subnet),
}
self._fields.update(self._path_fields)
super(Parameter, self).__init__(server_config, **kwargs)
if not any(
getattr(self, attr, None) for attr in self._path_fields):
raise TypeError(
'A value must be provided for any of "{0}" fields.'.format(
self._path_fields.keys())
)
self._parent_type = next(
attr for attr in self._path_fields if getattr(self, attr, None))
self._parent_id = getattr(self, self._parent_type).id
self._meta = {
'api_path': 'api/v2/{}s/{}/parameters'.format(
self._parent_type, self._parent_id),
'server_modes': ('sat'),
}

def read(self, entity=None, attrs=None, ignore=None, params=None):
"""Ignore path related fields as they're never returned by the server
and are only added to entity to be able to use proper path.
"""
if entity is None:
entity = type(self)(
self._server_config,
**{self._parent_type: self._parent_id}
)
if ignore is None:
ignore = set()
for field_name in self._path_fields:
ignore.add(field_name)
return super(Parameter, self).read(entity, attrs, ignore, params)


class Permission(Entity, EntityReadMixin, EntitySearchMixin):
"""A representation of a Permission entity."""

Expand Down
3 changes: 3 additions & 0 deletions nailgun/entity_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ def get_values(self):
attrs.pop('_server_config')
attrs.pop('_fields')
attrs.pop('_meta')
if '_path_fields' in attrs:
attrs.pop('_path_fields')
return attrs

def __repr__(self):
Expand All @@ -521,6 +523,7 @@ def __repr__(self):
u'{0}={1}'.format(key, repr(value))
for key, value
in self.get_values().items()
if not key.startswith('_')
)
)

Expand Down
38 changes: 38 additions & 0 deletions tests/test_entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ def test_init_succeeds(self):
(entities.OperatingSystemParameter, {'operatingsystem': 1}),
(entities.OverrideValue, {'smart_class_parameter': 1}),
(entities.OverrideValue, {'smart_variable': 1}),
(entities.Parameter, {'domain': 1}),
(entities.Parameter, {'host': 1}),
(entities.Parameter, {'hostgroup': 1}),
(entities.Parameter, {'location': 1}),
(entities.Parameter, {'operatingsystem': 1}),
(entities.Parameter, {'organization': 1}),
(entities.Parameter, {'subnet': 1}),
(entities.RepositorySet, {'product': 1}),
(entities.SyncPlan, {'organization': 1}),
])
Expand Down Expand Up @@ -219,6 +226,7 @@ def test_required_params(self):
entities.Image,
entities.OverrideValue,
entities.OperatingSystemParameter,
entities.Parameter,
entities.RepositorySet,
entities.SyncPlan,
):
Expand Down Expand Up @@ -991,6 +999,13 @@ def test_entity_arg(self):
entities.OperatingSystemParameter(self.cfg, operatingsystem=2),
entities.OverrideValue(self.cfg, smart_class_parameter=2),
entities.OverrideValue(self.cfg, smart_variable=2),
entities.Parameter(self.cfg, domain=2),
entities.Parameter(self.cfg, host=2),
entities.Parameter(self.cfg, hostgroup=2),
entities.Parameter(self.cfg, location=2),
entities.Parameter(self.cfg, operatingsystem=2),
entities.Parameter(self.cfg, organization=2),
entities.Parameter(self.cfg, subnet=2),
entities.RepositorySet(self.cfg, product=2),
entities.SyncPlan(self.cfg, organization=2),
):
Expand Down Expand Up @@ -1199,6 +1214,29 @@ def test_interface_ignore_arg(self):
# `call_args` is a two-tuple of (positional, keyword) args.
self.assertEqual(actual_ignore, read.call_args[0][2])

def test_parameter_ignore_arg(self):
"""Call :meth:`nailgun.entities.Parameter.read`.
Assert that entity`s predefined values of ``ignore`` are always
correctly passed on.
"""
parents = {
'domain', 'host', 'hostgroup', 'location', 'operatingsystem',
'organization', 'subnet'
}
for parent in parents:
with self.subTest(parent):
with mock.patch.object(EntityReadMixin, 'read') as read:
with mock.patch.object(
EntityReadMixin,
'read_json',
return_value={parent: 3},
):
entities.Parameter(
self.cfg, id=2, **{parent: 3}).read()
# `call_args` is a two-tuple of (positional, keyword) args.
self.assertEqual(parents, read.call_args[0][2])

def test_host_with_interface(self):
"""Call :meth:`nailgun.entities.Host.read`.
Expand Down
24 changes: 24 additions & 0 deletions tests/test_entity_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,30 @@ def test_entity_get_values(self):
values,
)

def test_entity_get_values_v2(self):
"""Test :meth:`nailgun.entity_mixins.Entity.get_values`, ensure
``_path_fields`` are never returned.
"""
for values in (
{},
{'id': gen_integer()},
{'name': gen_integer()},
{'number': gen_integer()},
{'name': gen_integer(), 'number': gen_integer()},
{
'id': gen_integer(),
'name': gen_integer(),
'number': gen_integer(),
},
):
entity = SampleEntity(self.cfg, **values)
# pylint:disable=protected-access,attribute-defined-outside-init
entity._path_fields = {'foo': 1}
self.assertEqual(
entity.get_values(),
values,
)

def test_path(self):
"""Test :meth:`nailgun.entity_mixins.Entity.path`."""
# e.g. 'https://sat.example.com/katello/api/v2'
Expand Down

0 comments on commit 2d306b4

Please sign in to comment.