From 5259051fc5d89727abd5991c6855e5da9dbb0ee4 Mon Sep 17 00:00:00 2001 From: Martin Kudlej Date: Fri, 24 Mar 2023 17:52:15 +0100 Subject: [PATCH 1/4] add support for mixed entity names in one collection add CMS files, sections, pages, layouts, partials add related unit tests --- tests/integration/conftest.py | 51 +++++++++ tests/integration/test_integration_cms.py | 131 ++++++++++++++++++++++ threescale_api/client.py | 40 +++++++ threescale_api/resources.py | 113 +++++++++++++++++++ threescale_api/utils.py | 2 +- 5 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 tests/integration/test_integration_cms.py diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 7d2e1f3..a9d4f84 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -497,3 +497,54 @@ def fields_definition(api, fields_definitions_params): entity = api.fields_definitions.create(fields_definitions_params) yield entity cleanup(entity) + + +@pytest.fixture(scope="module") +def cms_file_data(): + """CMS file fixture data""" + return dict(path=f"/path{get_suffix()}", downloadable=True) + + +@pytest.fixture(scope="module") +def cms_file_files(active_docs_body): + """CMS file fixture files. + File object can be used instead of file body 'active_docs_body', + see https://requests.readthedocs.io/en/latest/user/advanced/#post-multiple-multipart-encoded-files """ + return {'attachment': (f"name-{get_suffix()}", active_docs_body, 'application/json', {'Expires': '0'})} + + +@pytest.fixture(scope="module") +def cms_file(api, cms_file_data, cms_file_files): + """CMS file fixture""" + entity = api.cms_files.create(params={}, files=cms_file_files, data=cms_file_data) + yield entity + cleanup(entity) + + +@pytest.fixture(scope="module") +def cms_section_params(cms_file): + """CMS section fixture params""" + return dict(title=f"title-{get_suffix()}", public=True, partial_path=f"/path-{get_suffix()}", + cms_file_ids=[cms_file['id']]) + + +@pytest.fixture(scope="module") +def cms_section(api, cms_section_params): + """CMS section fixture""" + entity = api.cms_sections.create(cms_section_params) + yield entity + cleanup(entity) + + +@pytest.fixture(scope="module") +def cms_partial_params(): + """CMS partial fixture params""" + return dict(type='partial', system_name=f"sname-{get_suffix()}", draft=f"draft-{get_suffix()}") + + +@pytest.fixture(scope="module") +def cms_partial(api, cms_partial_params): + """CMS partial fixture""" + entity = api.cms_partials.create(cms_partial_params) + yield entity + cleanup(entity) diff --git a/tests/integration/test_integration_cms.py b/tests/integration/test_integration_cms.py new file mode 100644 index 0000000..1adb712 --- /dev/null +++ b/tests/integration/test_integration_cms.py @@ -0,0 +1,131 @@ +from tests.integration import asserts +from .asserts import assert_resource, assert_resource_params + + +# Files +def test_file_list(api, cms_file): + """ List all files. """ + assert len(list(api.cms_files.list())) >= 1 + + +def test_file_can_be_created(cms_file_data, cms_file): + """ Is file created properly? """ + assert_resource(cms_file) + assert_resource_params(cms_file, cms_file_data) + + +def test_file_can_be_read(api, cms_file_data, cms_file): + """ It is possible to get file by ID? """ + read = api.cms_files.read(cms_file.entity_id) + asserts.assert_resource(read) + asserts.assert_resource_params(read, cms_file_data) + + +def test_file_can_be_read_by_name(api, cms_file_data, cms_file): + """ It is possible to get file by name? """ + file_path = cms_file['path'] + read = api.cms_files[file_path] + asserts.assert_resource(read) + asserts.assert_resource_params(read, cms_file_data) + + +def test_file_can_be_updated(cms_file_data, cms_file): + """ Can be file object updated? """ + updated_path = cms_file['path'] = cms_file['path'] + 'up' + cms_file.update() + assert cms_file['path'] == updated_path + updated = cms_file.read() + assert updated['path'] == updated_path + assert cms_file['path'] == updated_path + + +# Sections +# builtin + +def test_builtin_section_list(api): + """ List all sections. """ + assert len(list(api.cms_builtin_sections.list())) >= 1 + + +def test_builtin_section_can_be_read(api): + """ It is possible to get section by ID? """ + cms_section = next(api.cms_builtin_sections.list()) + read = api.cms_sections.read(cms_section.entity_id) + asserts.assert_resource(read) + +# user + + +def test_section_list(api, cms_section): + """ List all sections. """ + assert len(list(api.cms_sections.list())) >= 1 + + +def test_section_can_be_created(cms_section_params, cms_section): + """ Is section created properly? """ + assert_resource(cms_section) + assert_resource_params(cms_section, cms_section_params) + + +def test_section_can_be_read(api, cms_section_params, cms_section): + """ It is possible to get section by ID? """ + read = api.cms_sections.read(cms_section.entity_id) + asserts.assert_resource(read) + asserts.assert_resource_params(read, cms_section_params) + + +def test_section_can_be_updated(cms_section_params, cms_section): + """ Can be section object updated? """ + updated_title = cms_section['title'] = cms_section['title'] + 'up' + cms_section.update() + assert cms_section['title'] == updated_title + updated = cms_section.read() + assert updated['title'] == updated_title + assert cms_section['title'] == updated_title + +# Partials +# builtin + + +def test_builtin_partials_list(api): + """ List all sections. """ + assert len(list(api.cms_builtin_partials.list())) >= 1 + + +def test_builtin_partial_can_be_read(api): + """ It is possible to get partial by ID? """ + cms_partial = next(api.cms_builtin_partials.list()) + read = api.cms_builtin_partials.read(cms_partial.entity_id) + asserts.assert_resource(read) + +# user + + +def test_partial_list(api, cms_partial): + """ List all user defined partials. """ + assert len(list(api.cms_partials.list())) >= 1 + + +def test_partial_can_be_created(cms_partial_params, cms_partial): + """ Is partial created properly? """ + assert_resource(cms_partial) + assert_resource_params(cms_partial, cms_partial_params) + + +def test_partial_can_be_read(api, cms_partial_params, cms_partial): + """ It is possible to get partial by ID? """ + read = api.cms_partials.read(cms_partial.entity_id) + asserts.assert_resource(read) + asserts.assert_resource_params(read, cms_partial_params) + + +def test_partial_can_be_updated(cms_partial_params, cms_partial): + """ Can be partial object updated? """ + updated_draft = cms_partial['draft'] = cms_partial['draft'] + 'up' + cms_partial.update() + assert cms_partial['draft'] == updated_draft + updated = cms_partial.read() + assert updated['draft'] == updated_draft + assert cms_partial['draft'] == updated_draft + +# # TODO pages, builtin_pages, layouts, template publishing diff --git a/threescale_api/client.py b/threescale_api/client.py index a44e597..dfc3f3f 100644 --- a/threescale_api/client.py +++ b/threescale_api/client.py @@ -50,6 +50,14 @@ def __init__(self, url: str, token: str, self._invoices = resources.Invoices(self, instance_klass=resources.Invoice) self._fields_definitions =\ resources.FieldsDefinitions(self, instance_klass=resources.FieldsDefinition) + self._cms_files = resources.CmsFiles(self, instance_klass=resources.CmsFile) + self._cms_sections = resources.CmsSections(self, instance_klass=resources.CmsSection) + self._cms_builtin_sections = resources.CmsBuiltinSections(self, instance_klass=resources.CmsSection) + self._cms_pages = resources.CmsPages(self, instance_klass=resources.CmsPage) + self._cms_builtin_pages = resources.CmsBuiltinPages(self, instance_klass=resources.CmsPage) + self._cms_layouts = resources.CmsLayouts(self, instance_klass=resources.CmsLayout) + self._cms_builtin_partials = resources.CmsBuiltinPartials(self, instance_klass=resources.CmsPartial) + self._cms_partials = resources.CmsPartials(self, instance_klass=resources.CmsPartial) if wait >= 0: self.wait_for_tenant() @@ -255,6 +263,38 @@ def invoices(self) -> resources.Invoices: def fields_definitions(self) -> resources.FieldsDefinitions: return self._fields_definitions + @property + def cms_files(self) -> resources.CmsFiles: + return self._cms_files + + @property + def cms_sections(self) -> resources.CmsSections: + return self._cms_sections + + @property + def cms_builtin_sections(self) -> resources.CmsBuiltinSections: + return self._cms_builtin_sections + + @property + def cms_pages(self) -> resources.CmsPages: + return self._cms_pages + + @property + def cms_builtin_pages(self) -> resources.CmsBuiltinPages: + return self._cms_builtin_pages + + @property + def cms_layouts(self) -> resources.CmsLayouts: + return self._cms_layouts + + @property + def cms_partials(self) -> resources.CmsPartials: + return self._cms_partials + + @property + def cms_builtin_partials(self) -> resources.CmsBuiltinPartials: + return self._cms_builtin_partials + class RestApiClient: def __init__(self, url: str, token: str, throws: bool = True, ssl_verify: bool = True): diff --git a/threescale_api/resources.py b/threescale_api/resources.py index f37c311..1085a3a 100644 --- a/threescale_api/resources.py +++ b/threescale_api/resources.py @@ -913,6 +913,94 @@ def __init__(self, *args, entity_name='fields_definition', def url(self) -> str: return self.threescale_client.admin_api_url + '/fields_definitions' + +class CmsClient(DefaultClient): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def _list(self, **kwargs): + if "page" in kwargs.get("params", {}): + return super()._list(**kwargs) + + pagenum = 1 + + kwargs = kwargs.copy() + if "params" not in kwargs: + kwargs["params"] = {} + + kwargs["params"]["page"] = pagenum + kwargs["params"]["per_page"] = 100 + + page = super()._list(**kwargs) + + while len(page): + for i in page: + yield i + pagenum += 1 + kwargs["params"]["page"] = pagenum + page = super()._list(**kwargs) + + def __iter__(self): + return self._list() + + +class CmsFiles(CmsClient): + def __init__(self, *args, entity_name='file', entity_collection='files', **kwargs): + super().__init__(*args, entity_name=entity_name, + entity_collection=entity_collection, **kwargs) + + @property + def url(self) -> str: + return self.threescale_client.admin_api_url + '/cms/files' + + +class CmsSections(CmsClient): + def __init__(self, *args, entity_name='section', entity_collection='sections', **kwargs): + super().__init__(*args, entity_name=entity_name, + entity_collection=entity_collection, **kwargs) + + @property + def url(self) -> str: + return self.threescale_client.admin_api_url + '/cms/sections' + +class CmsBuiltinSections(CmsSections): + def __init__(self, *args, entity_name='builtin_section', entity_collection='sections', **kwargs): + super().__init__(*args, entity_name=entity_name, + entity_collection=entity_collection, **kwargs) + + +class CmsTemplates(CmsClient): + def __init__(self, *args, entity_collection='templates', **kwargs): + super().__init__(*args, entity_collection=entity_collection, **kwargs) + + @property + def url(self) -> str: + return self.threescale_client.admin_api_url + '/cms/templates' + + +class CmsPages(CmsTemplates): + def __init__(self, *args, entity_name='page', **kwargs): + super().__init__(*args, entity_name=entity_name, **kwargs) + + +class CmsBuiltinPages(CmsTemplates): + def __init__(self, *args, entity_name='builtin_page', **kwargs): + super().__init__(*args, entity_name=entity_name, **kwargs) + + +class CmsLayouts(CmsTemplates): + def __init__(self, *args, entity_name='layout', **kwargs): + super().__init__(*args, entity_name=entity_name, **kwargs) + + +class CmsPartials(CmsTemplates): + def __init__(self, *args, entity_name='partial', **kwargs): + super().__init__(*args, entity_name=entity_name, **kwargs) + + +class CmsBuiltinPartials(CmsTemplates): + def __init__(self, *args, entity_name='builtin_partial', **kwargs): + super().__init__(*args, entity_name=entity_name, **kwargs) # Resources @@ -1462,3 +1550,28 @@ def __init__(self, entity_name='name', **kwargs): class DevPortalAuthProvider(DefaultResource): def __init__(self, entity_name='name', **kwargs): super().__init__(entity_name=entity_name, **kwargs) + + +class CmsFile(DefaultResource): + def __init__(self, entity_name='path', **kwargs): + super().__init__(entity_name=entity_name, **kwargs) + + +class CmsSection(DefaultResource): + def __init__(self, entity_name='id', **kwargs): + super().__init__(entity_name=entity_name, **kwargs) + + +class CmsPage(DefaultResource): + def __init__(self, entity_name='system_name', **kwargs): + super().__init__(entity_name=entity_name, **kwargs) + + +class CmsLayout(DefaultResource): + def __init__(self, entity_name='system_name', **kwargs): + super().__init__(entity_name=entity_name, **kwargs) + + +class CmsPartial(DefaultResource): + def __init__(self, entity_name='system_name', **kwargs): + super().__init__(entity_name=entity_name, **kwargs) diff --git a/threescale_api/utils.py b/threescale_api/utils.py index dba6648..203326b 100644 --- a/threescale_api/utils.py +++ b/threescale_api/utils.py @@ -23,7 +23,7 @@ def extract_response(response: requests.Response, entity: str = None, if collection and collection in extracted: extracted = extracted.get(collection) if isinstance(extracted, list): - return [value.get(entity) for value in extracted] + return [value.get(entity) for value in extracted if entity in value] if entity in extracted.keys(): return extracted.get(entity) return extracted From 64504bec7c5480ad5a8aa00a5f71a51e8cde058a Mon Sep 17 00:00:00 2001 From: Martin Kudlej Date: Tue, 4 Apr 2023 13:20:16 +0200 Subject: [PATCH 2/4] add pages change client pager and some lints --- tests/integration/conftest.py | 42 ++++++++-- tests/integration/test_integration_cms.py | 96 +++++++++++++++++++++-- threescale_api/resources.py | 7 +- 3 files changed, 130 insertions(+), 15 deletions(-) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index a9d4f84..77a4a33 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -502,7 +502,7 @@ def fields_definition(api, fields_definitions_params): @pytest.fixture(scope="module") def cms_file_data(): """CMS file fixture data""" - return dict(path=f"/path{get_suffix()}", downloadable=True) + return {"path": f"/path{get_suffix()}", "downloadable": True} @pytest.fixture(scope="module") @@ -510,7 +510,7 @@ def cms_file_files(active_docs_body): """CMS file fixture files. File object can be used instead of file body 'active_docs_body', see https://requests.readthedocs.io/en/latest/user/advanced/#post-multiple-multipart-encoded-files """ - return {'attachment': (f"name-{get_suffix()}", active_docs_body, 'application/json', {'Expires': '0'})} + return {"attachment": (f"name-{get_suffix()}", active_docs_body, "application/json", {"Expires": 0})} @pytest.fixture(scope="module") @@ -524,8 +524,8 @@ def cms_file(api, cms_file_data, cms_file_files): @pytest.fixture(scope="module") def cms_section_params(cms_file): """CMS section fixture params""" - return dict(title=f"title-{get_suffix()}", public=True, partial_path=f"/path-{get_suffix()}", - cms_file_ids=[cms_file['id']]) + return {"title": f"title-{get_suffix()}", "public": True, "partial_path": f"/path-{get_suffix()}", + "cms_file_ids": [cms_file['id']]} @pytest.fixture(scope="module") @@ -539,7 +539,7 @@ def cms_section(api, cms_section_params): @pytest.fixture(scope="module") def cms_partial_params(): """CMS partial fixture params""" - return dict(type='partial', system_name=f"sname-{get_suffix()}", draft=f"draft-{get_suffix()}") + return {"type": "partial", "system_name": f"sname-{get_suffix()}", "draft": f"draft-{get_suffix()}"} @pytest.fixture(scope="module") @@ -548,3 +548,35 @@ def cms_partial(api, cms_partial_params): entity = api.cms_partials.create(cms_partial_params) yield entity cleanup(entity) + + +@pytest.fixture(scope="module") +def cms_layout_params(cms_section): + """CMS layout fixture params""" + return {"type": "layout", "system_name": f"sname-{get_suffix()}", "draft": f"draft-{get_suffix()}", + "title": f"title-{get_suffix()}", "liquid_enabled": True} + +@pytest.fixture(scope="module") +def cms_layout(api, cms_layout_params): + """CMS layout fixture""" + entity = api.cms_layouts.create(cms_layout_params) + yield entity + cleanup(entity) + +@pytest.fixture(scope="module") +def cms_page_params(cms_section, cms_layout): + """CMS page fixture params""" + return {"type": "page", "system_name": f"sname-{get_suffix()}", "draft": f"draft-{get_suffix()}", + "title": f"title-{get_suffix()}", "path": f"/path-{get_suffix()}", + "section_name": f"section-{get_suffix()}", "section_id": cms_section['id'], + "layout_name": f"layout-{get_suffix()}", "layout_id": cms_layout['id'], + "liquid_enabled": True, "handler": "markdown", "tag_list": [1,2,3,4,5], + "content_type": "text/html"} + + +@pytest.fixture(scope="module") +def cms_page(api, cms_page_params): + """CMS page fixture""" + entity = api.cms_pages.create(cms_page_params) + yield entity + cleanup(entity) diff --git a/tests/integration/test_integration_cms.py b/tests/integration/test_integration_cms.py index 1adb712..53b6494 100644 --- a/tests/integration/test_integration_cms.py +++ b/tests/integration/test_integration_cms.py @@ -31,7 +31,8 @@ def test_file_can_be_read_by_name(api, cms_file_data, cms_file): def test_file_can_be_updated(cms_file_data, cms_file): """ Can be file object updated? """ - updated_path = cms_file['path'] = cms_file['path'] + 'up' + updated_path = cms_file['path'] + 'up' + cms_file['path'] = cms_file['path'] + 'up' cms_file.update() assert cms_file['path'] == updated_path updated = cms_file.read() @@ -44,12 +45,12 @@ def test_file_can_be_updated(cms_file_data, cms_file): def test_builtin_section_list(api): """ List all sections. """ - assert len(list(api.cms_builtin_sections.list())) >= 1 + assert len(api.cms_builtin_sections.list()) >= 1 def test_builtin_section_can_be_read(api): """ It is possible to get section by ID? """ - cms_section = next(api.cms_builtin_sections.list()) + cms_section = api.cms_builtin_sections.list()[-1] read = api.cms_sections.read(cms_section.entity_id) asserts.assert_resource(read) @@ -76,7 +77,8 @@ def test_section_can_be_read(api, cms_section_params, cms_section): def test_section_can_be_updated(cms_section_params, cms_section): """ Can be section object updated? """ - updated_title = cms_section['title'] = cms_section['title'] + 'up' + updated_title = cms_section['title'] + 'up' + cms_section['title'] = cms_section['title'] + 'up' cms_section.update() assert cms_section['title'] == updated_title updated = cms_section.read() @@ -94,7 +96,7 @@ def test_builtin_partials_list(api): def test_builtin_partial_can_be_read(api): """ It is possible to get partial by ID? """ - cms_partial = next(api.cms_builtin_partials.list()) + cms_partial = api.cms_builtin_partials.list()[-1] read = api.cms_builtin_partials.read(cms_partial.entity_id) asserts.assert_resource(read) @@ -121,11 +123,91 @@ def test_partial_can_be_read(api, cms_partial_params, cms_partial): def test_partial_can_be_updated(cms_partial_params, cms_partial): """ Can be partial object updated? """ - updated_draft = cms_partial['draft'] = cms_partial['draft'] + 'up' + updated_draft = cms_partial['draft'] + 'up' + cms_partial['draft'] = cms_partial['draft'] + 'up' cms_partial.update() assert cms_partial['draft'] == updated_draft updated = cms_partial.read() assert updated['draft'] == updated_draft assert cms_partial['draft'] == updated_draft -# # TODO pages, builtin_pages, layouts, template publishing +# TODO template publishing + +# Pages +# builtin + + +def test_builtin_pages_list(api): + """ List all sections. """ + assert len(list(api.cms_builtin_pages.list())) >= 1 + + +def test_builtin_page_can_be_read(api): + """ It is possible to get page by ID? """ + cms_page = api.cms_builtin_pages.list()[-1] + read = api.cms_builtin_pages.read(cms_page.entity_id) + asserts.assert_resource(read) + + +# user + + +def test_page_list(api, cms_page): + """ List all user defined pages. """ + assert len(list(api.cms_pages.list())) >= 1 + + +def test_page_can_be_created(cms_page_params, cms_page): + """ Is page created properly? """ + assert_resource(cms_page) + assert_resource_params(cms_page, cms_page_params) + + +def test_page_can_be_read(api, cms_page_params, cms_page): + """ It is possible to get page by ID? """ + read = api.cms_pages.read(cms_page.entity_id) + asserts.assert_resource(read) + asserts.assert_resource_params(read, cms_page_params) + + +def test_page_can_be_updated(cms_page_params, cms_page): + """ Can be page object updated? """ + updated_draft = cms_page['draft'] + 'up' + cms_page['draft'] = cms_page['draft'] + 'up' + cms_page.update() + assert cms_page['draft'] == updated_draft + updated = cms_page.read() + assert updated['draft'] == updated_draft + assert cms_page['draft'] == updated_draft + + +# Layouts + + +def test_layout_list(api, cms_layout): + """ List all user defined layouts. """ + assert len(list(api.cms_layouts.list())) >= 1 + + +def test_layout_can_be_created(cms_layout_params, cms_layout): + """ Is layout created properly? """ + assert_resource(cms_layout) + assert_resource_params(cms_layout, cms_layout_params) + + +def test_layout_can_be_read(api, cms_layout_params, cms_layout): + """ It is possible to get layout by ID? """ + read = api.cms_layouts.read(cms_layout.entity_id) + asserts.assert_resource(read) + asserts.assert_resource_params(read, cms_layout_params) + + +def test_layout_can_be_updated(cms_layout_params, cms_layout): + """ Can be layout object updated? """ + updated_draft = cms_layout['draft'] + 'up' + cms_layout['draft'] = cms_layout['draft'] + 'up' + cms_layout.update() + assert cms_layout['draft'] == updated_draft + updated = cms_layout.read() + assert updated['draft'] == updated_draft + assert cms_layout['draft'] == updated_draft diff --git a/threescale_api/resources.py b/threescale_api/resources.py index 1085a3a..d978443 100644 --- a/threescale_api/resources.py +++ b/threescale_api/resources.py @@ -921,7 +921,6 @@ def __init__(self, *args, **kwargs): def _list(self, **kwargs): if "page" in kwargs.get("params", {}): return super()._list(**kwargs) - pagenum = 1 kwargs = kwargs.copy() @@ -932,13 +931,15 @@ def _list(self, **kwargs): kwargs["params"]["per_page"] = 100 page = super()._list(**kwargs) + ret_list = page while len(page): - for i in page: - yield i pagenum += 1 kwargs["params"]["page"] = pagenum page = super()._list(**kwargs) + ret_list += page + + return ret_list def __iter__(self): return self._list() From b8c47febd84bc1a6d4a9dbc14b4da89a472bd18b Mon Sep 17 00:00:00 2001 From: Martin Kudlej Date: Tue, 4 Apr 2023 15:40:33 +0200 Subject: [PATCH 3/4] add publishing --- tests/integration/test_integration_cms.py | 45 +++++++++++++++++++---- threescale_api/client.py | 10 +++-- threescale_api/resources.py | 43 ++++++++++++++++++++-- 3 files changed, 82 insertions(+), 16 deletions(-) diff --git a/tests/integration/test_integration_cms.py b/tests/integration/test_integration_cms.py index 53b6494..4b10bb8 100644 --- a/tests/integration/test_integration_cms.py +++ b/tests/integration/test_integration_cms.py @@ -5,7 +5,7 @@ # Files def test_file_list(api, cms_file): """ List all files. """ - assert len(list(api.cms_files.list())) >= 1 + assert len(api.cms_files.list()) >= 1 def test_file_can_be_created(cms_file_data, cms_file): @@ -59,7 +59,7 @@ def test_builtin_section_can_be_read(api): def test_section_list(api, cms_section): """ List all sections. """ - assert len(list(api.cms_sections.list())) >= 1 + assert len(api.cms_sections.list()) >= 1 def test_section_can_be_created(cms_section_params, cms_section): @@ -91,7 +91,7 @@ def test_section_can_be_updated(cms_section_params, cms_section): def test_builtin_partials_list(api): """ List all sections. """ - assert len(list(api.cms_builtin_partials.list())) >= 1 + assert len(api.cms_builtin_partials.list()) >= 1 def test_builtin_partial_can_be_read(api): @@ -105,7 +105,7 @@ def test_builtin_partial_can_be_read(api): def test_partial_list(api, cms_partial): """ List all user defined partials. """ - assert len(list(api.cms_partials.list())) >= 1 + assert len(api.cms_partials.list()) >= 1 def test_partial_can_be_created(cms_partial_params, cms_partial): @@ -131,7 +131,16 @@ def test_partial_can_be_updated(cms_partial_params, cms_partial): assert updated['draft'] == updated_draft assert cms_partial['draft'] == updated_draft -# TODO template publishing + +def test_partial_publish(cms_partial): + """ Test publishing of partials. """ + assert cms_partial.entity.get('published', None) is None + draft = cms_partial['draft'] + cms_partial = cms_partial.publish() + # assert draft == cms_partial['draft'] bug + # assert cms_partial['published'] == cms_partial['draft'] bug + assert draft == cms_partial['published'] + # Pages # builtin @@ -139,7 +148,7 @@ def test_partial_can_be_updated(cms_partial_params, cms_partial): def test_builtin_pages_list(api): """ List all sections. """ - assert len(list(api.cms_builtin_pages.list())) >= 1 + assert len(api.cms_builtin_pages.list()) >= 1 def test_builtin_page_can_be_read(api): @@ -154,7 +163,7 @@ def test_builtin_page_can_be_read(api): def test_page_list(api, cms_page): """ List all user defined pages. """ - assert len(list(api.cms_pages.list())) >= 1 + assert len(api.cms_pages.list()) >= 1 def test_page_can_be_created(cms_page_params, cms_page): @@ -181,12 +190,22 @@ def test_page_can_be_updated(cms_page_params, cms_page): assert cms_page['draft'] == updated_draft +def test_page_publish(cms_page): + """ Test publishing of pages. """ + assert cms_page.entity.get('published', None) is None + draft = cms_page['draft'] + cms_page = cms_page.publish() + # assert draft == cms_page['draft'] bug + # assert cms_page['published'] == cms_page['draft'] bug + assert draft == cms_page['published'] + + # Layouts def test_layout_list(api, cms_layout): """ List all user defined layouts. """ - assert len(list(api.cms_layouts.list())) >= 1 + assert len(api.cms_layouts.list()) >= 1 def test_layout_can_be_created(cms_layout_params, cms_layout): @@ -211,3 +230,13 @@ def test_layout_can_be_updated(cms_layout_params, cms_layout): updated = cms_layout.read() assert updated['draft'] == updated_draft assert cms_layout['draft'] == updated_draft + + +def test_layout_publish(cms_layout): + """ Test publishing of layouts. """ + assert cms_layout.entity.get('published', None) is None + draft = cms_layout['draft'] + cms_layout = cms_layout.publish() + # assert draft == cms_layout['draft'] bug + # assert cms_layout['published'] == cms_layout['draft'] bug + assert draft == cms_layout['published'] diff --git a/threescale_api/client.py b/threescale_api/client.py index dfc3f3f..6ccc5ea 100644 --- a/threescale_api/client.py +++ b/threescale_api/client.py @@ -52,11 +52,13 @@ def __init__(self, url: str, token: str, resources.FieldsDefinitions(self, instance_klass=resources.FieldsDefinition) self._cms_files = resources.CmsFiles(self, instance_klass=resources.CmsFile) self._cms_sections = resources.CmsSections(self, instance_klass=resources.CmsSection) - self._cms_builtin_sections = resources.CmsBuiltinSections(self, instance_klass=resources.CmsSection) + self._cms_builtin_sections =\ + resources.CmsBuiltinSections(self, instance_klass=resources.CmsSection) self._cms_pages = resources.CmsPages(self, instance_klass=resources.CmsPage) self._cms_builtin_pages = resources.CmsBuiltinPages(self, instance_klass=resources.CmsPage) self._cms_layouts = resources.CmsLayouts(self, instance_klass=resources.CmsLayout) - self._cms_builtin_partials = resources.CmsBuiltinPartials(self, instance_klass=resources.CmsPartial) + self._cms_builtin_partials =\ + resources.CmsBuiltinPartials(self, instance_klass=resources.CmsPartial) self._cms_partials = resources.CmsPartials(self, instance_klass=resources.CmsPartial) if wait >= 0: @@ -282,11 +284,11 @@ def cms_pages(self) -> resources.CmsPages: @property def cms_builtin_pages(self) -> resources.CmsBuiltinPages: return self._cms_builtin_pages - + @property def cms_layouts(self) -> resources.CmsLayouts: return self._cms_layouts - + @property def cms_partials(self) -> resources.CmsPartials: return self._cms_partials diff --git a/threescale_api/resources.py b/threescale_api/resources.py index d978443..1571fbe 100644 --- a/threescale_api/resources.py +++ b/threescale_api/resources.py @@ -915,6 +915,7 @@ def url(self) -> str: class CmsClient(DefaultClient): + """ Client for all cms api endpoints. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -946,6 +947,7 @@ def __iter__(self): class CmsFiles(CmsClient): + """ Client for files. """ def __init__(self, *args, entity_name='file', entity_collection='files', **kwargs): super().__init__(*args, entity_name=entity_name, entity_collection=entity_collection, **kwargs) @@ -956,6 +958,7 @@ def url(self) -> str: class CmsSections(CmsClient): + """ Client for sections. """ def __init__(self, *args, entity_name='section', entity_collection='sections', **kwargs): super().__init__(*args, entity_name=entity_name, entity_collection=entity_collection, **kwargs) @@ -964,13 +967,17 @@ def __init__(self, *args, entity_name='section', entity_collection='sections', * def url(self) -> str: return self.threescale_client.admin_api_url + '/cms/sections' + class CmsBuiltinSections(CmsSections): - def __init__(self, *args, entity_name='builtin_section', entity_collection='sections', **kwargs): + """ Client for builtin sections. """ + def __init__(self, *args, entity_name='builtin_section', entity_collection='sections', + **kwargs): super().__init__(*args, entity_name=entity_name, entity_collection=entity_collection, **kwargs) class CmsTemplates(CmsClient): + """ Client for templates. """ def __init__(self, *args, entity_collection='templates', **kwargs): super().__init__(*args, entity_collection=entity_collection, **kwargs) @@ -978,28 +985,41 @@ def __init__(self, *args, entity_collection='templates', **kwargs): def url(self) -> str: return self.threescale_client.admin_api_url + '/cms/templates' + def publish(self, entity_id, **kwargs): + """ Publish template with entity_id """ + log.info("[PUBLISH] " + f"{entity_id}") + url = self._entity_url(entity_id) + '/publish' + response = self.rest.put(url=url, **kwargs) + instance = self._create_instance(response=response) + return instance + class CmsPages(CmsTemplates): + """ Client for pages """ def __init__(self, *args, entity_name='page', **kwargs): super().__init__(*args, entity_name=entity_name, **kwargs) class CmsBuiltinPages(CmsTemplates): + """ Client for builtin pages. """ def __init__(self, *args, entity_name='builtin_page', **kwargs): super().__init__(*args, entity_name=entity_name, **kwargs) class CmsLayouts(CmsTemplates): + """ Client for layouts """ def __init__(self, *args, entity_name='layout', **kwargs): super().__init__(*args, entity_name=entity_name, **kwargs) class CmsPartials(CmsTemplates): + """ Client for partials """ def __init__(self, *args, entity_name='partial', **kwargs): super().__init__(*args, entity_name=entity_name, **kwargs) class CmsBuiltinPartials(CmsTemplates): + """ Client for builtin partials """ def __init__(self, *args, entity_name='builtin_partial', **kwargs): super().__init__(*args, entity_name=entity_name, **kwargs) # Resources @@ -1554,25 +1574,40 @@ def __init__(self, entity_name='name', **kwargs): class CmsFile(DefaultResource): + """ Resource for file """ def __init__(self, entity_name='path', **kwargs): super().__init__(entity_name=entity_name, **kwargs) class CmsSection(DefaultResource): + """ Resource for section. """ def __init__(self, entity_name='id', **kwargs): super().__init__(entity_name=entity_name, **kwargs) -class CmsPage(DefaultResource): +class CmsTemplate(DefaultResource): + """ Resource for templates """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def publish(self, **kwargs): + """ Publish template resource """ + return self.client.publish(entity_id=self.entity_id, **kwargs) + + +class CmsPage(CmsTemplate): + """ Resource for page """ def __init__(self, entity_name='system_name', **kwargs): super().__init__(entity_name=entity_name, **kwargs) -class CmsLayout(DefaultResource): +class CmsLayout(CmsTemplate): + """ Resource for layout """ def __init__(self, entity_name='system_name', **kwargs): super().__init__(entity_name=entity_name, **kwargs) -class CmsPartial(DefaultResource): +class CmsPartial(CmsTemplate): + """ Resource for partials """ def __init__(self, entity_name='system_name', **kwargs): super().__init__(entity_name=entity_name, **kwargs) From dd27263e759f34cebb853b1e36a0be67e59991af Mon Sep 17 00:00:00 2001 From: Martin Kudlej Date: Thu, 27 Apr 2023 22:25:19 +0200 Subject: [PATCH 4/4] changes for new version of API --- tests/integration/conftest.py | 20 ++++---- tests/integration/test_integration_cms.py | 59 +++++++++++++++++------ threescale_api/client.py | 6 --- threescale_api/resources.py | 35 +++++++++----- threescale_api/utils.py | 2 +- 5 files changed, 79 insertions(+), 43 deletions(-) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 77a4a33..e9c8716 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -500,9 +500,9 @@ def fields_definition(api, fields_definitions_params): @pytest.fixture(scope="module") -def cms_file_data(): +def cms_file_data(cms_section): """CMS file fixture data""" - return {"path": f"/path{get_suffix()}", "downloadable": True} + return {"path": f"/path{get_suffix()}", "downloadable": True, 'section_id': cms_section['id']} @pytest.fixture(scope="module") @@ -522,10 +522,9 @@ def cms_file(api, cms_file_data, cms_file_files): @pytest.fixture(scope="module") -def cms_section_params(cms_file): +def cms_section_params(): """CMS section fixture params""" - return {"title": f"title-{get_suffix()}", "public": True, "partial_path": f"/path-{get_suffix()}", - "cms_file_ids": [cms_file['id']]} + return {"title": f"title-{get_suffix()}", "public": True, "partial_path": f"/path-{get_suffix()}"} @pytest.fixture(scope="module") @@ -539,7 +538,7 @@ def cms_section(api, cms_section_params): @pytest.fixture(scope="module") def cms_partial_params(): """CMS partial fixture params""" - return {"type": "partial", "system_name": f"sname-{get_suffix()}", "draft": f"draft-{get_suffix()}"} + return {"system_name": f"sname-{get_suffix()}", "draft": f"draft-{get_suffix()}"} @pytest.fixture(scope="module") @@ -553,8 +552,8 @@ def cms_partial(api, cms_partial_params): @pytest.fixture(scope="module") def cms_layout_params(cms_section): """CMS layout fixture params""" - return {"type": "layout", "system_name": f"sname-{get_suffix()}", "draft": f"draft-{get_suffix()}", - "title": f"title-{get_suffix()}", "liquid_enabled": True} + return {"system_name": f"sname-{get_suffix()}", "draft": f"draft-{get_suffix()}", + "title": f"title-{get_suffix()}", "liquid_enabled": True, "section_id": cms_section['id']} @pytest.fixture(scope="module") def cms_layout(api, cms_layout_params): @@ -566,12 +565,11 @@ def cms_layout(api, cms_layout_params): @pytest.fixture(scope="module") def cms_page_params(cms_section, cms_layout): """CMS page fixture params""" - return {"type": "page", "system_name": f"sname-{get_suffix()}", "draft": f"draft-{get_suffix()}", + return {"system_name": f"sname-{get_suffix()}", "draft": f"draft-{get_suffix()}", "title": f"title-{get_suffix()}", "path": f"/path-{get_suffix()}", "section_name": f"section-{get_suffix()}", "section_id": cms_section['id'], "layout_name": f"layout-{get_suffix()}", "layout_id": cms_layout['id'], - "liquid_enabled": True, "handler": "markdown", "tag_list": [1,2,3,4,5], - "content_type": "text/html"} + "liquid_enabled": True, "handler": "markdown", "content_type": "text/html"} @pytest.fixture(scope="module") diff --git a/tests/integration/test_integration_cms.py b/tests/integration/test_integration_cms.py index 4b10bb8..2249bcf 100644 --- a/tests/integration/test_integration_cms.py +++ b/tests/integration/test_integration_cms.py @@ -1,4 +1,6 @@ +import pytest from tests.integration import asserts +from threescale_api import errors from .asserts import assert_resource, assert_resource_params @@ -33,6 +35,9 @@ def test_file_can_be_updated(cms_file_data, cms_file): """ Can be file object updated? """ updated_path = cms_file['path'] + 'up' cms_file['path'] = cms_file['path'] + 'up' + # TODO https://issues.redhat.com/browse/THREESCALE-9571 + for item in "created_at", "updated_at", "url", "title", "content_type": + cms_file.pop(item) cms_file.update() assert cms_file['path'] == updated_path updated = cms_file.read() @@ -41,20 +46,6 @@ def test_file_can_be_updated(cms_file_data, cms_file): # Sections -# builtin - -def test_builtin_section_list(api): - """ List all sections. """ - assert len(api.cms_builtin_sections.list()) >= 1 - - -def test_builtin_section_can_be_read(api): - """ It is possible to get section by ID? """ - cms_section = api.cms_builtin_sections.list()[-1] - read = api.cms_sections.read(cms_section.entity_id) - asserts.assert_resource(read) - -# user def test_section_list(api, cms_section): @@ -79,12 +70,26 @@ def test_section_can_be_updated(cms_section_params, cms_section): """ Can be section object updated? """ updated_title = cms_section['title'] + 'up' cms_section['title'] = cms_section['title'] + 'up' + # TODO https://issues.redhat.com/browse/THREESCALE-9571 + for item in "created_at", "updated_at": + cms_section.pop(item) cms_section.update() assert cms_section['title'] == updated_title updated = cms_section.read() assert updated['title'] == updated_title assert cms_section['title'] == updated_title + +# # bug!!! TODO https://issues.redhat.com/browse/THREESCALE-9572 +# def test_builtin_section_delete(api): +# """It is not possible to delete section partial.""" +# with pytest.raises(errors.ApiClientError) as exc_info: +# api.cms_sections.list()[0].delete() +# assert exc_info.value.code == 423 +# # TODO +# # assert exc_info.value.code == 400 + + # Partials # builtin @@ -100,6 +105,14 @@ def test_builtin_partial_can_be_read(api): read = api.cms_builtin_partials.read(cms_partial.entity_id) asserts.assert_resource(read) +def test_builtin_partial_delete(api): + """It is not possible to delete builtin partial.""" + with pytest.raises(errors.ApiClientError) as exc_info: + api.cms_builtin_partials.list()[0].delete() + assert exc_info.value.code == 423 + # TODO https://issues.redhat.com/browse/THREESCALE-9572 + # assert exc_info.value.code == 400 + # user @@ -125,6 +138,9 @@ def test_partial_can_be_updated(cms_partial_params, cms_partial): """ Can be partial object updated? """ updated_draft = cms_partial['draft'] + 'up' cms_partial['draft'] = cms_partial['draft'] + 'up' + # TODO https://issues.redhat.com/browse/THREESCALE-9571 + for item in "created_at", "updated_at", "published": + cms_partial.pop(item) cms_partial.update() assert cms_partial['draft'] == updated_draft updated = cms_partial.read() @@ -158,6 +174,15 @@ def test_builtin_page_can_be_read(api): asserts.assert_resource(read) +def test_builtin_page_delete(api): + """It is not possible to delete builtin page.""" + with pytest.raises(errors.ApiClientError) as exc_info: + api.cms_builtin_pages.list()[0].delete() + assert exc_info.value.code == 423 + # TODO https://issues.redhat.com/browse/THREESCALE-9572 + # assert exc_info.value.code == 400 + + # user @@ -183,6 +208,9 @@ def test_page_can_be_updated(cms_page_params, cms_page): """ Can be page object updated? """ updated_draft = cms_page['draft'] + 'up' cms_page['draft'] = cms_page['draft'] + 'up' + # TODO https://issues.redhat.com/browse/THREESCALE-9571 + for item in "created_at", "updated_at", "hidden", "published": + cms_page.pop(item) cms_page.update() assert cms_page['draft'] == updated_draft updated = cms_page.read() @@ -225,6 +253,9 @@ def test_layout_can_be_updated(cms_layout_params, cms_layout): """ Can be layout object updated? """ updated_draft = cms_layout['draft'] + 'up' cms_layout['draft'] = cms_layout['draft'] + 'up' + # TODO https://issues.redhat.com/browse/THREESCALE-9571 + for item in "created_at", "updated_at", "published": + cms_layout.pop(item) cms_layout.update() assert cms_layout['draft'] == updated_draft updated = cms_layout.read() diff --git a/threescale_api/client.py b/threescale_api/client.py index 6ccc5ea..6e40251 100644 --- a/threescale_api/client.py +++ b/threescale_api/client.py @@ -52,8 +52,6 @@ def __init__(self, url: str, token: str, resources.FieldsDefinitions(self, instance_klass=resources.FieldsDefinition) self._cms_files = resources.CmsFiles(self, instance_klass=resources.CmsFile) self._cms_sections = resources.CmsSections(self, instance_klass=resources.CmsSection) - self._cms_builtin_sections =\ - resources.CmsBuiltinSections(self, instance_klass=resources.CmsSection) self._cms_pages = resources.CmsPages(self, instance_klass=resources.CmsPage) self._cms_builtin_pages = resources.CmsBuiltinPages(self, instance_klass=resources.CmsPage) self._cms_layouts = resources.CmsLayouts(self, instance_klass=resources.CmsLayout) @@ -273,10 +271,6 @@ def cms_files(self) -> resources.CmsFiles: def cms_sections(self) -> resources.CmsSections: return self._cms_sections - @property - def cms_builtin_sections(self) -> resources.CmsBuiltinSections: - return self._cms_builtin_sections - @property def cms_pages(self) -> resources.CmsPages: return self._cms_pages diff --git a/threescale_api/resources.py b/threescale_api/resources.py index 1571fbe..6678f5a 100644 --- a/threescale_api/resources.py +++ b/threescale_api/resources.py @@ -919,6 +919,12 @@ class CmsClient(DefaultClient): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + def _extract_resource(self, response, collection) -> Union[List, Dict]: + extracted = response.json() + if self._entity_collection and self._entity_collection in extracted: + extracted = extracted.get(self._entity_collection) + return extracted + def _list(self, **kwargs): if "page" in kwargs.get("params", {}): return super()._list(**kwargs) @@ -948,7 +954,7 @@ def __iter__(self): class CmsFiles(CmsClient): """ Client for files. """ - def __init__(self, *args, entity_name='file', entity_collection='files', **kwargs): + def __init__(self, *args, entity_name='file', entity_collection='collection', **kwargs): super().__init__(*args, entity_name=entity_name, entity_collection=entity_collection, **kwargs) @@ -959,7 +965,7 @@ def url(self) -> str: class CmsSections(CmsClient): """ Client for sections. """ - def __init__(self, *args, entity_name='section', entity_collection='sections', **kwargs): + def __init__(self, *args, entity_name='section', entity_collection='collection', **kwargs): super().__init__(*args, entity_name=entity_name, entity_collection=entity_collection, **kwargs) @@ -968,17 +974,9 @@ def url(self) -> str: return self.threescale_client.admin_api_url + '/cms/sections' -class CmsBuiltinSections(CmsSections): - """ Client for builtin sections. """ - def __init__(self, *args, entity_name='builtin_section', entity_collection='sections', - **kwargs): - super().__init__(*args, entity_name=entity_name, - entity_collection=entity_collection, **kwargs) - - class CmsTemplates(CmsClient): """ Client for templates. """ - def __init__(self, *args, entity_collection='templates', **kwargs): + def __init__(self, *args, entity_collection='collection', **kwargs): super().__init__(*args, entity_collection=entity_collection, **kwargs) @property @@ -993,6 +991,21 @@ def publish(self, entity_id, **kwargs): instance = self._create_instance(response=response) return instance + def list(self, **kwargs) -> List['DefaultResource']: + """List all entities + Args: + **kwargs: Optional parameters + Returns(List['DefaultResource]): List of resources + """ + log.info(self._log_message("[LIST] List", args=kwargs)) + instance = self.select_by(type=self._entity_name, **kwargs) + return instance + + def create(self, params: dict = None, + *args, **kwargs) -> 'DefaultResource': + params.update({'type': self._entity_name}) + return super().create(params=params, **kwargs) + class CmsPages(CmsTemplates): """ Client for pages """ diff --git a/threescale_api/utils.py b/threescale_api/utils.py index 203326b..dba6648 100644 --- a/threescale_api/utils.py +++ b/threescale_api/utils.py @@ -23,7 +23,7 @@ def extract_response(response: requests.Response, entity: str = None, if collection and collection in extracted: extracted = extracted.get(collection) if isinstance(extracted, list): - return [value.get(entity) for value in extracted if entity in value] + return [value.get(entity) for value in extracted] if entity in extracted.keys(): return extracted.get(entity) return extracted