diff --git a/nailgun/entities.py b/nailgun/entities.py index 66aec3c2..714712ca 100644 --- a/nailgun/entities.py +++ b/nailgun/entities.py @@ -8754,3 +8754,55 @@ def sync(self, synchronous=True, timeout=None, **kwargs): kwargs.update(self._server_config.get_client_kwargs()) response = client.put(self.path('sync'), **kwargs) return _handle_response(response, self._server_config, synchronous, timeout) + + +class TablePreferences( + Entity, + EntityCreateMixin, + EntityDeleteMixin, + EntityReadMixin, + EntitySearchMixin, + EntityUpdateMixin, +): + """A representation of a Table Preference entity.""" + + def __init__(self, server_config=None, **kwargs): + _check_for_value('user', kwargs) + self._fields = { + 'name': entity_fields.StringField(), + 'columns': entity_fields.ListField(), + 'created_at': entity_fields.DateTimeField(), + 'updated_at': entity_fields.DateTimeField(), + } + self._path_fields = { + 'user': entity_fields.OneToOneField(User), + } + self._fields.update(self._path_fields) + self.user = kwargs.get('user') + if isinstance(self.user, int): + self._meta = { + 'api_path': f'/api/v2/users/{self.user}/table_preferences', + } + else: + self._meta = { + 'api_path': f'/api/v2/users/{self.user.id}/table_preferences', + } + super().__init__(server_config, **kwargs) + + 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. + """ + entity = entity or self.entity_with_parent(user=self.user) + if ignore is None: + ignore = set() + ignore.add('user') + return super().read(entity, attrs, ignore, params) + + def search(self, fields=None, query=None, filters=None): + """List/search for TablePreferences. + Field 'user' is only used for path and is not returned. + """ + return super().search( + fields=fields, query=query, filters=filters, path_fields={'user': self.user} + ) diff --git a/nailgun/entity_mixins.py b/nailgun/entity_mixins.py index f7f97be0..2084c5ce 100644 --- a/nailgun/entity_mixins.py +++ b/nailgun/entity_mixins.py @@ -1308,7 +1308,7 @@ def search_normalize(self, results): normalized.append(attrs) return normalized - def search(self, fields=None, query=None, filters=None): + def search(self, fields=None, query=None, filters=None, path_fields={}): """Search for entities. At its simplest, this method searches for all entities of a given kind. @@ -1392,11 +1392,11 @@ def search(self, fields=None, query=None, filters=None): entities = [] for result in results: try: - entity = type(self)(self._server_config, **result) + entity = type(self)(self._server_config, **path_fields, **result) except TypeError: # in the event that an entity's init is overwritten # with a positional server_config - entity = type(self)(**result) + entity = type(self)(**path_fields, **result) entities.append(entity) if filters is not None: entities = self.search_filter(entities, filters) diff --git a/tests/test_entities.py b/tests/test_entities.py index c4049c49..aa655c9a 100644 --- a/tests/test_entities.py +++ b/tests/test_entities.py @@ -215,6 +215,7 @@ def test_init_succeeds(self): (entities.SSHKey, {'user': 1}), (entities.SyncPlan, {'organization': 1}), (entities.TemplateInput, {'template': 1}), + (entities.TablePreferences, {'user': 1}), ] ) for entity, params in entities_: @@ -2880,6 +2881,52 @@ def test_read(self): self.assertEqual(template_input.template.id, self.template_input_data['template']) +class TablePreferencesTestCase(TestCase): + """Tests for :class:`nailgun.entities.TablePreferences`.""" + + def setUp(self): + self.sc = config.ServerConfig('some url') + + def test_read(self): + user_id = gen_integer(min_value=1) + user = entities.User(self.sc, id=user_id) + entity = entities.TablePreferences(self.sc, user=user) + self.assertEqual(entity.user.id, user_id) + self.assertIn(f'/{user_id}/', entity._meta['api_path']) + read_json_patcher = mock.patch.object(entity, 'read_json') + read_json = read_json_patcher.start() + table_id = gen_integer(min_value=1) + read_json.return_value = { + 'id': table_id, + 'name': 'testname', + 'columns': ['testcol'], + 'created_at': '2023-06-01 12:38:05 UTC', + 'updated_at': '2023-06-01 12:38:05 UTC', + } + res = entity.read() + read_json_patcher.stop() + self.assertEqual(read_json.call_count, 1) + self.assertEqual(res.name, 'testname') + self.assertEqual(res.id, table_id) + self.assertEqual(res.columns, ['testcol']) + + def test_search(self): + user_id = gen_integer(min_value=1) + user = entities.User(self.sc, id=user_id) + ret = { + 'total': 1, + 'page': 1, + 'results': [{'id': 1, 'name': 'testname', 'columns': ['testcol']}], + } + entity = entities.TablePreferences(self.sc, user=user) + with mock.patch.object(entity, 'search_json', return_value=ret): + res = entity.search() + self.assertEqual(len(res), 1) + self.assertEqual(res[0].name, 'testname') + self.assertEqual(res[0].id, 1) + self.assertEqual(res[0].columns, ['testcol']) + + class HostGroupTestCase(TestCase): """Tests for :class:`nailgun.entities.HostGroup`."""