From 92e2a31a60122cbac7101cb84bc505484c554e46 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Wed, 23 Aug 2023 11:28:38 +0530 Subject: [PATCH] Feat/cs 40545 metadata class implemetation (#15) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Metadata class implementation * Deleted sast-scan.yml file * Fixed PR comments * Code optimisation and handling custom error exception * Removed uids * Feat/cs 40549 roles class implementation (#16) * Created roles class with unit testcases, mock test cases and api test cases * Updated coverage report github workflow file * Added coverage install command * Added pytest install command * Updated stack class unit test file * Added html coverage * Added coverage reports and visualizations * Removed visualisation * Generating coverage report in svg format * Added coverage-badge install command * Fixed PR comments and code optimisation * Added coverage reports graphical visualisation in github page * Created readme file * changed variable name * Updated readme file * Update README.md * Added auditlog class and license file (#17) * Added auditlog class and license file * Added test case for invalid inputs * commented the idiom It Allows You to Execute Code When the File Runs as a Script, but Not When It’s Imported as a Module --------- Co-authored-by: Shailesh Mishra --------- Co-authored-by: Shailesh Mishra --- .DS_Store | Bin 8196 -> 8196 bytes .github/workflows/unit-test.yml | 79 ++--- .talismanrc | 59 ++++ LICENSE | 21 ++ README.md | 107 +++++- contentstack_management/_api_client.py | 4 +- contentstack_management/_errors.py | 10 +- contentstack_management/auditlogs/auditlog.py | 65 ++++ contentstack_management/metadata/metadata.py | 224 ++++++++++++ contentstack_management/roles/roles.py | 296 ++++++++++++++++ contentstack_management/stack/stack.py | 14 +- requirements.txt | 3 +- setup.py | 4 +- tests/api/auditlogs/test_auditlogs_api.py | 44 +++ tests/api/metadata/test_metadata_api.py | 106 ++++++ tests/api/roles/test_roles_api.py | 216 ++++++++++++ tests/cred.py | 8 +- .../{test_assets.py => test_assets_mock.py} | 0 tests/mock/auditlogs/test_auditlogs_mock.py | 46 +++ tests/mock/metadata/test_metadata_mock.py | 120 +++++++ tests/mock/roles/test_roles_mock.py | 230 +++++++++++++ tests/resources/mock_auditlogs/fetch.json | 81 +++++ tests/resources/mock_auditlogs/find.json | 321 ++++++++++++++++++ tests/resources/mock_metadata/create.json | 28 ++ tests/resources/mock_metadata/delete.json | 3 + tests/resources/mock_metadata/fetch.json | 27 ++ tests/resources/mock_metadata/find.json | 29 ++ tests/resources/mock_metadata/update.json | 28 ++ tests/resources/mock_roles/create.json | 89 +++++ tests/resources/mock_roles/delete.json | 3 + tests/resources/mock_roles/fetch.json | 57 ++++ tests/resources/mock_roles/find.json | 108 ++++++ tests/resources/mock_roles/update.json | 95 ++++++ .../{test_assets.py => test_assets_unit.py} | 10 +- tests/unit/auditlogs/test_auditlog_unit.py | 65 ++++ tests/unit/metadata/test_metadata_unit.py | 118 +++++++ tests/unit/roles/test_roles_unit.py | 220 ++++++++++++ tests/unit/stack/test_stack.py | 7 +- 38 files changed, 2884 insertions(+), 61 deletions(-) create mode 100644 LICENSE create mode 100644 contentstack_management/auditlogs/auditlog.py create mode 100644 contentstack_management/metadata/metadata.py create mode 100644 contentstack_management/roles/roles.py create mode 100644 tests/api/auditlogs/test_auditlogs_api.py create mode 100644 tests/api/metadata/test_metadata_api.py create mode 100644 tests/api/roles/test_roles_api.py rename tests/mock/assets/{test_assets.py => test_assets_mock.py} (100%) create mode 100644 tests/mock/auditlogs/test_auditlogs_mock.py create mode 100644 tests/mock/metadata/test_metadata_mock.py create mode 100644 tests/mock/roles/test_roles_mock.py create mode 100644 tests/resources/mock_auditlogs/fetch.json create mode 100644 tests/resources/mock_auditlogs/find.json create mode 100644 tests/resources/mock_metadata/create.json create mode 100644 tests/resources/mock_metadata/delete.json create mode 100644 tests/resources/mock_metadata/fetch.json create mode 100644 tests/resources/mock_metadata/find.json create mode 100644 tests/resources/mock_metadata/update.json create mode 100644 tests/resources/mock_roles/create.json create mode 100644 tests/resources/mock_roles/delete.json create mode 100644 tests/resources/mock_roles/fetch.json create mode 100644 tests/resources/mock_roles/find.json create mode 100644 tests/resources/mock_roles/update.json rename tests/unit/assets/{test_assets.py => test_assets_unit.py} (97%) create mode 100644 tests/unit/auditlogs/test_auditlog_unit.py create mode 100644 tests/unit/metadata/test_metadata_unit.py create mode 100644 tests/unit/roles/test_roles_unit.py diff --git a/.DS_Store b/.DS_Store index a1d77deab4dbb56f67355b4bff5c22c03b9c6270..bc03c7abdb6db7d1f18f7b28437d386905400faf 100644 GIT binary patch delta 609 zcmcgoO-lk%6ulFEOkp~2#?ro+Nl79^%lSYF(w1!siZ*Q|O)454&9a3kEnH*~1b5%6 zT~L`|%l7?>*8PB1fxV|K#2?Uu3-_J-?s?~&Myipz+Zy)+00$rdSdeY)Jv!S?-o_IK zJ76D*a0~}*gwukI9%0JC&C!KAMuc}vl1*As3`v)~s%v3I)iivPk_;3oJEsNHDjfFt zLBSM>pqnW8KnUIvUw{$8eYbaL2f#xPDo}tlt$oNt4h>QEIN=D+=*@&I)ltEUD9;RG znTv8UV_F%?(ZQHhT9~X>H8rfn^;%e|#?^ROF%o*MHp))~qpREL6Ek11uJA*WY@TI8cU8!&G))BW;zuT-)jYwqaMC{r}Gi_X`@rq=End delta 204 zcmZp1XmOa}&n?Knz`)4BAi%(ovN2{g`({=SHV#I?$rb`KleGi{C#MQ*VU(O4CnU|9 z%#h1aJo%K60u$rU$y|c5lYa||PktpNIJsCvm5pIN(3p9XuZbA3G0K9Oe4@In-C&l3 zsI}}*5bHk}09gzSYz##Vr3`rtdJF{&m6N+f coverage_report.txt + coverage xml --include="tests/unit/*" -o coverage.xml + coverage html --include="tests/unit/*" + coverage-badge -o coverage.svg + + - name: Upload coverage artifact + uses: actions/upload-artifact@v2 with: - parallel-finished: true \ No newline at end of file + name: coverage-report + path: | + coverage_report.txt + coverage.xml + htmlcov + coverage.svg + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + diff --git a/.talismanrc b/.talismanrc index 1c17e5e..00c8d50 100644 --- a/.talismanrc +++ b/.talismanrc @@ -150,3 +150,62 @@ fileignoreconfig: - filename: contentstack_management/workflows/workflows.py checksum: 85e3a2ee0ced2fd1787f74f15388a674130ec2b2fe433f19ca96c2cc968508a5 version: "" + +fileignoreconfig: +- filename: tests/resources/mock_metadata/create.json + checksum: 4d8942e449701b1599dca9485ef7a6d6e65d44d71ebabd04335147132c93a807 +- filename: tests/resources/mock_metadata/update.json + checksum: 8bb19547a9ecbbac023db0dde82c6d69a4893953c6b6c0118839e40810f9a61d +- filename: tests/mock/metadata/test_metadata_mock.py + checksum: fc239da7691c242e8bbf0fa3db04cb4d024c841b1a8e227177294ec055566b05 +- filename: tests/resources/mock_metadata/fetch.json + checksum: 305bb32cd8dc0d3bbe4dc1708e3a6b2e770f92504c807b1c2747511e567d58b1 +- filename: tests/resources/mock_metadata/find.json + checksum: b69fe03ca10170c7d00cca0d0784e8bb334307a9872f9a57f0ec75cc6def302a +- filename: tests/unit/metadata/test_metadata_unit.py + checksum: 990c7e67627204c671da5eb2043630b635cfaa9d2f9ef4c46cddd2fd3ebedb19 +- filename: tests/api/metadata/test_metadata_api.py + checksum: 72e6676565b99227b0e87ef9569fdd8298143ab4601259b46d05b7cc09f1e912 +- filename: contentstack_management/metadata/metadata.py + checksum: aa059e6dee28d87be47e7aa2cb92fd39fcd0f2f89366d27236ddf8ecc51e8f7b +version: "" + +fileignoreconfig: +- filename: tests/resources/mock_roles/fetch.json + checksum: 9207921c9508fe82e59bf6101d1fa99b2b9a62b6b864f36d90f5819a15fdbaa0 +- filename: tests/resources/mock_roles/create.json + checksum: d59d280188a9d57a596fbcd7746b718ef5cf2bc9ad187537f9caa730bc2c03af +- filename: tests/resources/mock_roles/find.json + checksum: 70b7016d8dcf96474c298cf3052d91d83cb3b154d16ebb3e167cb85cefa3a760 +- filename: tests/resources/mock_roles/update.json + checksum: f83955fa82e9e8f84aaccc0b7f866116e07d9115deb3ffac3f1406e9c1800745 +- filename: tests/api/roles/test_roles_api.py + checksum: 36d5ccd16ee1785b2ca774303bd3d4d24ab15846bb1b67037b26e152b117b4b2 +- filename: tests/mock/roles/test_roles_mock.py + checksum: a6ca0282a3df204bf783b8bf73dbdac02e256177df2b000f95ba9d528f2322b4 +- filename: tests/unit/roles/test_roles_unit.py + checksum: 3294f71a285ba9e2e61a6d75884498618a7934b76993b1c0ff385937bb5ed74f +- filename: contentstack_management/roles/roles.py + checksum: 373a75c8df71801a8b39cfa992bc9296b7ff5398bf4de4f990f6fe1977841a3c +version: "" + +fileignoreconfig: +- filename: tests/unit/stack/test_stack.py + checksum: 882d1d8d76848a5192c0d042948a04e4edbabac3b47ee725689181ff4ec3393b +version: "" +fileignoreconfig: +- filename: README.md + checksum: 769bf6032aa18764c5924a0f71b911a1b66258a0b04f1e0aef4650c486b0a050 +version: "" +fileignoreconfig: +- filename: README.md + checksum: 45ea945d901c8a47144e8eb7106e49b1d121400cf47cbc7e5134f62a236b8da5 +version: "" +fileignoreconfig: +- filename: tests/mock/auditlogs/test_auditlogs_mock.py + checksum: 6be6e351200624863cbfc443d3215242f03b0b06adc06649d119bee80b7608a3 +- filename: tests/api/auditlogs/test_auditlogs_api.py + checksum: ff37d395fe03a822775e73a1c867cfcbbe14a75e37e4cb8a6d09f19fd790da7e +- filename: tests/unit/auditlogs/test_auditlog_unit.py + checksum: 1b8b24cc7f7921d209b983a99c0305d5fb27c1fe8e3fc74f1d525a7327eba830 +version: "" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b805319 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2012 - 2023 Contentstack. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 7b33bc8..02de87d 100644 --- a/README.md +++ b/README.md @@ -1 +1,106 @@ -# contentstack-management-python \ No newline at end of file +[![Contentstack](https://www.contentstack.com/docs/static/images/contentstack.png)](https://www.contentstack.com/) + +## Contentstack Management Python SDK + +Contentstack is a headless CMS with an API-first approach. It is a CMS that developers can use to build powerful cross-platform applications in their favorite languages. All you have to do is build your application frontend, and Contentstack will take care of the rest. [Read More](https://www.contentstack.com/). + +This SDK uses the [Content Management API](https://www.contentstack.com/docs/developers/apis/content-management-api/) (CMA). The CMA is used to manage the content of your Contentstack account. This includes creating, updating, deleting, and fetching content of your account. To use the CMA, you will need to authenticate your users with a [Management Token](https://www.contentstack.com/docs/developers/create-tokens/about-management-tokens) or an [Authtoken](https://www.contentstack.com/docs/developers/apis/content-management-api/#how-to-get-authtoken). Read more about it in [Authentication](https://www.contentstack.com/docs/developers/apis/content-management-api/#authentication). + +Note: By using CMA, you can execute GET requests for fetching content. However, we strongly recommend that you always use the [Content Delivery API](https://www.contentstack.com/docs/developers/apis/content-delivery-api/) to deliver content to your web or mobile properties. + +### Prerequisite + +You will need python 3 installed on your machine. You can install it +from [here](https://www.python.org/ftp/python/3.7.4/python-3.7.4-macosx10.9.pkg). + +### Installation +#### Install contentstack pip + +```python +pip install contentstack_management +``` +To import the SDK, use the following command: +```python +from contentstack_management import contentstack + +client = contentstack.ContentstackClient() +``` + +### Authentication +To use this SDK, you need to authenticate your users by using the Authtoken, credentials, or Management Token (stack-level token). +### Authtoken + +An **Authtoken** is a read-write token used to make authorized CMA requests, and it is a **user-specific** token. +```python +client = contentstack.ContentstackClient(authtoken= 'authtoken') +``` +### Login +To Login to Contentstack by using credentials, you can use the following lines of code: +```python +client.login(email="email", password="password") + +``` + +### Management Token +**Management Tokens** are **stack-level** tokens, with no users attached to them. + +```python +result = client.stack(api_key = 'api_key', management_token= 'management_token' ).content_type('content_type_uid') +.fetch().json() +print(result) +``` +### Contentstack Management Python SDK: 5-minute Quickstart +#### Initializing Your SDK: +To use the Python CMA SDK, you need to first initialize it. To do this, use the following code: + +```python +from contentstack_management import contentstack + +client = contentstack.ContentstackClient(authtoken= 'authtoken') +``` +#### Fetch Stack Detail +Use the following lines of code to fetch your stack detail using this SDK: +```python +result = client.stack(api_key= 'api_key').fetch().json() +print(result) +``` + +#### Create Entry +To create an entry in a specific content type of a stack, use the following lines of code: + +```python +entry = { + title: 'Sample Entry', + url: '/sampleEntry' +} + +result = client.stack(api_key= 'api_key').content_types('content_type_uid').entry().create(entry) +print(result.json()) +``` + +#### Create Asset +The following lines of code can be used to upload assets to your stack: + +```python +asset = { + upload: 'path/to/file', + title: 'Asset Title' +} +asset = client().stack(api_key='api_key').assets() +result = asset.upload(asset) +``` + +### Helpful Links + +- [Contentstack Website](https://www.contentstack.com/) +- [Official Documentation](https://contentstack.com/docs) +- [Content Management API Docs](https://www.contentstack.com/docs/developers/apis/content-management-api) + +### The MIT License (MIT) +Copyright © 2012-2023 [Contentstack](https://www.contentstack.com/). All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/contentstack_management/_api_client.py b/contentstack_management/_api_client.py index 264ebcc..82b2299 100644 --- a/contentstack_management/_api_client.py +++ b/contentstack_management/_api_client.py @@ -80,7 +80,7 @@ def get(self, path, params=None, headers=None): # self.headers = headers or {} return self._call_request('GET', url, headers=headers, params=params) - def put(self, path, data=None, params=None, json_data=None, headers=None): + def put(self, path, data=None, params=None, json_data=None, headers=None, files=None): """ The function sends a PUT request to a specified URL with optional data, parameters, JSON data, and headers. @@ -102,7 +102,7 @@ def put(self, path, data=None, params=None, json_data=None, headers=None): url = f"{self.endpoint}{path}" # headers = headers or {} - return self._call_request('PUT', url, headers=headers, params=params, data=data, json_data=json_data) + return self._call_request('PUT', url, headers=headers, params=params, data=data, json_data=json_data, files=files) def post(self, path, data=None, json_data=None, headers=None, params=None, files=None): """ diff --git a/contentstack_management/_errors.py b/contentstack_management/_errors.py index 9c69c25..7c944df 100644 --- a/contentstack_management/_errors.py +++ b/contentstack_management/_errors.py @@ -1,2 +1,10 @@ -class Error(Exception): +class ArgumentException(Exception): + pass + def __init__(self, f, *args): + super().__init__(args) + self.f = f + + def __str__(self): + return self.f + diff --git a/contentstack_management/auditlogs/auditlog.py b/contentstack_management/auditlogs/auditlog.py new file mode 100644 index 0000000..ffb11f9 --- /dev/null +++ b/contentstack_management/auditlogs/auditlog.py @@ -0,0 +1,65 @@ +"""This class takes a base URL as an argument when it's initialized, +which is the endpoint for the RESTFUL API that we'll be interacting with. +The create(), read(), update(), and delete() methods each correspond to +the CRUD operations that can be performed on the API """ + +import json +from ..common import Parameter +from urllib.parse import quote +from .._errors import ArgumentException + +class Auditlog(Parameter): + """ + This class takes a base URL as an argument when it's initialized, + which is the endpoint for the RESTFUL API that + we'll be interacting with. The create(), read(), update(), and delete() + methods each correspond to the CRUD + operations that can be performed on the API """ + + def __init__(self, client, log_item_uid: str): + self.client = client + self.log_item_uid = log_item_uid + super().__init__(self.client) + + self.path = "audit-logs" + + def find(self): + """ + The "Get audit log" request is used to retrieve the audit log of a stack. + :return: Json, with auditlog details. + + ------------------------------- + [Example:] + + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack("api_key").auditlog().find().json() + + ------------------------------- + """ + return self.client.get(self.path, headers = self.client.headers) + + + + def fetch(self): + """ + The "Get audit log item" request is used to retrieve a specific item from the audit log of a stack. + :return: Json, with auditlog details. + ------------------------------- + [Example:] + + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack('api_key').auditlog('log_item_uid').fetch().json() + + ------------------------------- + """ + self.validate_uid() + url = f"{self.path}/{self.log_item_uid}" + return self.client.get(url, headers = self.client.headers) + + def validate_uid(self): + if self.log_item_uid is None or '': + raise ArgumentException('Log item Uid is required') + + \ No newline at end of file diff --git a/contentstack_management/metadata/metadata.py b/contentstack_management/metadata/metadata.py new file mode 100644 index 0000000..d7891f2 --- /dev/null +++ b/contentstack_management/metadata/metadata.py @@ -0,0 +1,224 @@ +"""This class takes a base URL as an argument when it's initialized, +which is the endpoint for the RESTFUL API that we'll be interacting with. +The create(), read(), update(), and delete() methods each correspond to +the CRUD operations that can be performed on the API """ + +import json +from ..common import Parameter +from urllib.parse import quote +from .._errors import ArgumentException + +class Metadata(Parameter): + """ + This class takes a base URL as an argument when it's initialized, + which is the endpoint for the RESTFUL API that + we'll be interacting with. The create(), read(), update(), and delete() + methods each correspond to the CRUD + operations that can be performed on the API """ + + def __init__(self, client, metadata_uid: str): + self.client = client + self.metadata_uid = metadata_uid + super().__init__(self.client) + + self.path = "metadata" + + def find(self): + """ + The Get All Metadata request returns comprehensive information of all the metadata attached to all the entries and assets in your stack. + :return: Json, with Metadata details. + + ------------------------------- + [Example:] + + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack("api_key").metadata().find().json() + + ------------------------------- + """ + return self.client.get(self.path, headers = self.client.headers) + + + + def fetch(self): + """ + The Get metadata request fetches the metadata attached to a specific asset or entry of a stack. + :return: Json, with Metadata details. + ------------------------------- + [Example:] + + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack('api_key').metadata('metadata_uid').fetch().json() + + ------------------------------- + """ + self.validate_uid() + url = f"{self.path}/{self.metadata_uid}" + return self.client.get(url, headers = self.client.headers) + + + def create(self, data: dict): + """ + The Create metadata request lets you create metadata for a specific asset or entry. + Whenever you create metadata for an entry or asset, you need to specify the extension to which it will be connected. + + :param data: The `data` parameter is the payload that you want to send in the request body. It + should be a dictionary or a JSON serializable object that you want to send as the request body + :return: Json, with Metadata details. + + ------------------------------- + [Example:] + >>> data ={ + >>> "metadata": { + >>> "entity_uid": "entity_uid", + >>> "type": "entry", + >>> "_content_type_uid": "sample_content", + >>> "extension_uid": "extension_uid", + >>> "presets": [{ + >>> "uid": "presents_uid", + >>> "name": "Test1", + >>> "options": { + >>> } + >>> }] + >>> } + >>> } + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack('api_key').metadata().create(data).json() + + ------------------------------- + """ + + data = json.dumps(data) + return self.client.post(self.path, headers = self.client.headers, data=data) + + def update(self, data: dict): + """ + The Update metadata request lets you update the metadata for a specific entry or asset. + + :param data: The `data` parameter is the data that you want to update. It should be a dictionary + or an object that can be serialized to JSON + :return: Json, with updated Metadata details. + ------------------------------- + [Example:] + >>> data = { + >>> "metadata": { + >>> "entity_uid": "entity_uid", + >>> "type": "entry", + >>> "extension_uid": "extension_uid", + >>> "locale": "en_us", + >>> "_content_type_uid": "_content_type_uid", + >>> "presets": [{ + >>> "uid": "presets_uid", + >>> "name": "test1", + >>> "options": {} + >>> }, + >>> { + >>> "name": "Test3", + >>> "uid": "presets_uid", + >>> "options": { + >>> "quality": "100", + >>> "transform": { + >>> "height": 500, + >>> "width": 500 + >>> }, + >>> "image-type": "jpeg", + >>> "focal-point": { + >>> "x": 0, + >>> "y": 0 + >>> } + >>> } + >>> } + >>> ] + >>> } + >>> } + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack('api_key').metadata("metadata_uid").update(data).json() + + ------------------------------- + """ + self.validate_uid() + url = f"{self.path}/{self.metadata_uid}" + data = json.dumps(data) + return self.client.put(url, headers = self.client.headers, data=data) + + + def delete(self): + """ + The Delete metadata request lets you delete the metadata associated with a specific entry or asset. + :return: The delete() method returns the status code and message as a response. + + ------------------------------- + [Example:] + + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack('api_key').metadata('metadata_uid').delete().json() + + ------------------------------- + """ + self.validate_uid() + url = f"{self.path}/{self.metadata_uid}" + return self.client.delete(url, headers = self.client.headers) + + def publish(self, data: dict): + """ + The Publish metadata request lets you publish the metadata associated with a specific entry or asset. + + :return: Json, with updated Metadata details. + ------------------------------- + [Example:] + >>> data = { + >>> "metadata": { + >>> "environments": [ + >>> "test" + >>> ], + >>> "locales": [ + >>> "en-us" + >>> ] + >>> } + >>> } + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack('api_key').metadata('metadata_uid').publish(data).json() + + ------------------------------- + """ + self.validate_uid() + url = f"{self.path}/{self.metadata_uid}/publish" + data = json.dumps(data) + return self.client.post(url, headers = self.client.headers, data = data) + + def unpublish(self, data: dict): + """ + The Unpublish metadata request lets you unpublish the metadata associated with a specific entry or asset. + :return: Json, with updated Metadata details. + ------------------------------- + [Example:] + >>> data = { + >>> "metadata": { + >>> "environments": [ + >>> "test" + >>> ], + >>> "locales": [ + >>> "en-us" + >>> ] + >>> } + >>> } + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack('api_key').metadata('metadata_uid').unpublish(data).json() + + ------------------------------- + """ + self.validate_uid() + url = f"{self.path}/{self.metadata_uid}/unpublish" + data = json.dumps(data) + return self.client.post(url, headers = self.client.headers, data = data) + + def validate_uid(self): + if self.metadata_uid is None or '': + raise ArgumentException("Metadata Uid is required") \ No newline at end of file diff --git a/contentstack_management/roles/roles.py b/contentstack_management/roles/roles.py new file mode 100644 index 0000000..5b7d969 --- /dev/null +++ b/contentstack_management/roles/roles.py @@ -0,0 +1,296 @@ +"""This class takes a base URL as an argument when it's initialized, +which is the endpoint for the RESTFUL API that we'll be interacting with. +The create(), read(), update(), and delete() methods each correspond to +the CRUD operations that can be performed on the API """ + +import json +from ..common import Parameter +from urllib.parse import quote +from .._errors import ArgumentException + +class Roles(Parameter): + """ + This class takes a base URL as an argument when it's initialized, + which is the endpoint for the RESTFUL API that + we'll be interacting with. The create(), read(), update(), and delete() + methods each correspond to the CRUD + operations that can be performed on the API """ + + def __init__(self, client, role_uid: str): + self.client = client + self.role_uid = role_uid + super().__init__(self.client) + + self.path = "roles" + + def find(self): + """ + The "Get all roles" request returns comprehensive information about all roles created in a stack. + :return: Json, with roles details. + + ------------------------------- + [Example:] + + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack("api_key").roles().find().json() + + ------------------------------- + """ + return self.client.get(self.path, headers = self.client.headers) + + + + def fetch(self): + """ + The "Get a single role" request returns comprehensive information on a specific role. + :return: Json, with roles details. + ------------------------------- + [Example:] + + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack('api_key').roles('role_uid').fetch().json() + + ------------------------------- + """ + self.validate_uid() + url = f"{self.path}/{self.role_uid}" + return self.client.get(url, headers = self.client.headers) + + + def create(self, data: dict): + """ + The Create roles request lets you create role of your stack. + + :param data: The `data` parameter is the payload that you want to send in the request body. It + should be a dictionary or a JSON serializable object that you want to send as the request body + :return: Json, with roles details. + + ------------------------------- + [Example:] + >>> data ={ + >>> "role":{ + >>> "name":"testRole", + >>> "description":"This is a test role.", + >>> "rules":[ + >>> { + >>> "module":"branch", + >>> "branches":[ + >>> "main" + >>> ], + >>> "acl":{ + >>> "read":true + >>> } + >>> }, + >>> { + >>> "module":"branch_alias", + >>> "branch_aliases":[ + >>> "deploy" + >>> ], + >>> "acl":{ + >>> "read":true + >>> } + >>> }, + >>> { + >>> "module":"content_type", + >>> "content_types":[ + >>> "$all" + >>> ], + >>> "acl":{ + >>> "read":true, + >>> "sub_acl":{ + >>> "read":true + >>> } + >>> } + >>> }, + >>> { + >>> "module":"asset", + >>> "assets":[ + >>> "$all" + >>> ], + >>> "acl":{ + >>> "read":true, + >>> "update":true, + >>> "publish":true, + >>> "delete":true + >>> } + >>> }, + >>> { + >>> "module":"folder", + >>> "folders":[ + >>> "$all" + >>> ], + >>> "acl":{ + >>> "read":true, + >>> "sub_acl":{ + >>> "read":true + >>> } + >>> } + >>> }, + >>> { + >>> "module":"environment", + >>> "environments":[ + >>> "$all" + >>> ], + >>> "acl":{ + >>> "read":true + >>> } + >>> }, + >>> { + >>> "module":"locale", + >>> "locales":[ + >>> "en-us" + >>> ], + >>> "acl":{ + >>> "read":true + >>> } + >>> } + >>> ] + >>> } + >>> } + + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack('api_key').roles().create(data).json() + + ------------------------------- + """ + + data = json.dumps(data) + return self.client.post(self.path, headers = self.client.headers, data=data) + + def update(self, data: dict): + """ + The "Update role" request lets you modify an existing role of your stack. However, + the pre-existing system roles cannot be modified. + + :param data: The `data` parameter is the data that you want to update. It should be a dictionary + or an object that can be serialized to JSON + :return: Json, with updated roles details. + ------------------------------- + [Example:] + >>> data = { + >>> "role":{ + >>> "name":"sampleRole", + >>> "description":"This is a test role.", + >>> "rules":[ + >>> { + >>> "module":"branch", + >>> "branches":[ + >>> "main" + >>> ], + >>> "acl":{ + >>> "read":true + >>> } + >>> }, + >>> { + >>> "module":"branch_alias", + >>> "branch_aliases":[ + >>> "deploy" + >>> ], + >>> "acl":{ + >>> "read":true + >>> } + >>> }, + >>> { + >>> "module":"content_type", + >>> "content_types":[ + >>> "$all" + >>> ], + >>> "acl":{ + >>> "read":true, + >>> "sub_acl":{ + >>> "read":true + >>> } + >>> } + >>> }, + >>> { + >>> "module":"asset", + >>> "assets":[ + >>> "$all" + >>> ], + >>> "acl":{ + >>> "read":true, + >>> "update":true, + >>> "publish":true, + >>> "delete":true + >>> } + >>> }, + >>> { + >>> "module":"folder", + >>> "folders":[ + >>> "$all" + >>> ], + >>> "acl":{ + >>> "read":true, + >>> "update":true, + >>> "publish":true, + >>> "delete":true, + >>> "sub_acl":{ + >>> "read":true, + >>> "update":true, + >>> "publish":true, + >>> "delete":true + >>> } + >>> } + >>> }, + >>> { + >>> "module":"environment", + >>> "environments":[ + >>> "$all" + >>> ], + >>> "acl":{ + >>> "read":true + >>> } + >>> }, + >>> { + >>> "module":"locale", + >>> "locales":[ + >>> "$all" + >>> ], + >>> "acl":{ + >>> "read":true + >>> } + >>> } + >>> ], + >>> "uid":"blt5a570885da41c710" + >>> } + >>> } + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = client.stack('api_key').roles("role_uid").update(data).json() + + ------------------------------- + """ + + self.validate_uid() + url = f"{self.path}/{self.role_uid}" + data = json.dumps(data) + return self.client.put(url, headers = self.client.headers, data=data) + + + def delete(self): + """ + The "Delete role" call deletes an existing role from your stack. + :return: The delete() method returns the status code and message as a response. + + ------------------------------- + [Example:] + + >>> from contentstack_management import contentstack + >>> client = contentstack.client(authtoken='your_authtoken') + >>> result = result = client.stack('api_key').roles('role_uid').delete().json() + + ------------------------------- + """ + + + self.validate_uid() + url = f"{self.path}/{self.role_uid}" + return self.client.delete(url, headers = self.client.headers) + + def validate_uid(self): + if self.role_uid is None or '': + raise ArgumentException('Role Uid is required') + \ No newline at end of file diff --git a/contentstack_management/stack/stack.py b/contentstack_management/stack/stack.py index 492a15c..151195e 100644 --- a/contentstack_management/stack/stack.py +++ b/contentstack_management/stack/stack.py @@ -9,6 +9,9 @@ from ..webhook.webhook import Webhooks from ..workflows.workflows import Workflows from ..metadata.metadata import Metadata +from ..roles.roles import Roles +from ..auditlogs.auditlog import Auditlog + class Stack(Parameter): @@ -324,5 +327,12 @@ def assets(self, asset_uid=None, branch=None): def workflows(self, workflow_uid=None): return Workflows(self.client, workflow_uid) - - + + def metadata(self, metadata_uid: str = None): + return Metadata(self.client, metadata_uid) + + def roles(self, roles_uid: str = None): + return Roles(self.client, roles_uid) + + def auditlog(self, log_item_uid: str = None): + return Auditlog(self.client, log_item_uid) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index da96826..4c42cf2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ python-dotenv~=1.0.0 setuptools==68.0.0 requests~=2.31.0 -pylint \ No newline at end of file +pylint +bson>=0.5.9 diff --git a/setup.py b/setup.py index ab604e7..d27a626 100644 --- a/setup.py +++ b/setup.py @@ -14,14 +14,14 @@ long_description_content_type="text/markdown", url="https://github.com/contentstack/contentstack-management-python", author="Sunil", - author_email="sunil,lakshman@contentstack.com", + author_email="sunil.lakshman@contentstack.com", license="MIT", classifiers=[ "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.10", "Operating System :: OS Independent", ], - install_requires=["bson >= 0.5.10"], + install_requires=["bson >= 0.5.9", "requests >= 2.5.4"], extras_require={ "dev": ["pytest>=7.0", "twine>=4.0.2", "dotenv>=0.0.5"], }, diff --git a/tests/api/auditlogs/test_auditlogs_api.py b/tests/api/auditlogs/test_auditlogs_api.py new file mode 100644 index 0000000..484f420 --- /dev/null +++ b/tests/api/auditlogs/test_auditlogs_api.py @@ -0,0 +1,44 @@ +import os +import unittest + +from dotenv import load_dotenv + +from contentstack_management import contentstack +from tests.cred import get_credentials + +credentials = get_credentials() +username = credentials["username"] +password = credentials["password"] +host = credentials["host"] +api_key = credentials["api_key"] +log_item_uid = credentials["log_item_uid"] + + +class auditlogesApiTests(unittest.TestCase): + + def setUp(self): + self.client = contentstack.ContentstackClient(host=host) + self.client.login(username, password) + + def test_get_all_auditloges(self): + response = self.client.stack(api_key).auditlog().find() + self.assertEqual(response.status_code, 200) + + def test_get_a_auditlog(self): + response = self.client.stack(api_key).auditlog(log_item_uid).fetch() + self.assertEqual(response.status_code, 200) + + def test_get_all_auditloges_with_params(self): + query = self.client.stack(api_key).auditlog() + query.add_param("include_branch", True) + response = query.find() + self.assertEqual(response.status_code, 200) + + def test_get_a_auditlog_with_params(self): + query = self.client.stack(api_key).auditlog(log_item_uid) + query.add_param("include_branch", True) + response = query.fetch() + self.assertEqual(response.status_code, 200) + + if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/tests/api/metadata/test_metadata_api.py b/tests/api/metadata/test_metadata_api.py new file mode 100644 index 0000000..841c16c --- /dev/null +++ b/tests/api/metadata/test_metadata_api.py @@ -0,0 +1,106 @@ +import os +import unittest +from dotenv import load_dotenv +from contentstack_management import contentstack +from tests.cred import get_credentials + +credentials = get_credentials() +username = credentials["username"] +password = credentials["password"] +host = credentials["host"] +api_key = credentials["api_key"] +metadata_uid = credentials["metadata_uid"] + +class MetadataApiTests(unittest.TestCase): + + def setUp(self): + self.client = contentstack.ContentstackClient(host=host) + self.client.login(username, password) + + def test_get_all_metadata(self): + response = self.client.stack(api_key).metadata().find() + self.assertEqual(response.request.url, f"{self.client.endpoint}metadata") + self.assertEqual(response.status_code, 200) + + def test_get_a_metadata(self): + response = self.client.stack(api_key).metadata(metadata_uid).fetch() + self.assertEqual(response.request.url, + f"{self.client.endpoint}metadata/{metadata_uid}") + self.assertEqual(response.status_code, 200) + + + def test_create(self): + data = { + "metadata": { + "entity_uid": "entity_uid", + "type": "entry", + "_content_type_uid": "_content_type_uid", + "extension_uid": "extension_uid", + "presets": [{ + "uid": "presents_uid", + "name": "Test1", + "options": { + + } + }] + } + } + response = self.client.stack(api_key).metadata().create(data) + self.assertEqual(response.request.url, f"{self.client.endpoint}metadata") + self.assertEqual(response.status_code, 201) + + def test_update_metadata(self): + data = { + "metadata": { + "entity_uid": "entity_uid", + "type": "entry", + "_content_type_uid": "_content_type_uid", + "extension_uid": "extension_uid", + "presets": [{ + "uid": "presents_uid", + "name": "Test1", + "options": { + + } + }] + } + } + response = self.client.stack(api_key).metadata(metadata_uid).update(data) + self.assertEqual(response.request.url, + f"{self.client.endpoint}metadata/{metadata_uid}") + self.assertEqual(response.status_code, 200) + + + def test_delete_metadata(self): + response = self.client.stack(api_key).metadata(metadata_uid).delete() + self.assertEqual(response.status_code, 200) + + def test_publish(self): + data = { + "metadata": { + "environments": [ + "environment_name" + ], + "locales": [ + "en-us" + ] + } + } + response = self.client.stack(api_key).metadata(metadata_uid).publish(data) + self.assertEqual(response.status_code, 200) + + def test_unpublish(self): + data = { + "metadata": { + "environments": [ + "environment_name" + ], + "locales": [ + "en-us" + ] + } + } + response = self.client.stack(api_key).metadata(metadata_uid).unpublish(data) + self.assertEqual(response.status_code, 200) + + \ No newline at end of file diff --git a/tests/api/roles/test_roles_api.py b/tests/api/roles/test_roles_api.py new file mode 100644 index 0000000..0a154e0 --- /dev/null +++ b/tests/api/roles/test_roles_api.py @@ -0,0 +1,216 @@ +import os +import unittest +from dotenv import load_dotenv +from contentstack_management import contentstack +from tests.cred import get_credentials + +credentials = get_credentials() +username = credentials["username"] +password = credentials["password"] +host = credentials["host"] +api_key = credentials["api_key"] +role_uid = credentials["role_uid"] + +class rolesApiTests(unittest.TestCase): + + def setUp(self): + self.client = contentstack.ContentstackClient(host=host) + self.client.login(username, password) + + def test_get_all_roles(self): + response = self.client.stack(api_key).roles().find() + self.assertEqual(response.request.url, f"{self.client.endpoint}roles") + self.assertEqual(response.status_code, 200) + + def test_get_a_roles(self): + response = self.client.stack(api_key).roles(role_uid).fetch() + self.assertEqual(response.request.url, + f"{self.client.endpoint}roles/{role_uid}") + self.assertEqual(response.status_code, 200) + + + def test_create(self): + data = { + "role":{ + "name":"testRole", + "description":"This is a test role.", + "rules":[ + { + "module":"branch", + "branches":[ + "main" + ], + "acl":{ + "read":True + } + }, + { + "module":"branch_alias", + "branch_aliases":[ + "deploy" + ], + "acl":{ + "read":True + } + }, + { + "module":"content_type", + "content_types":[ + "$all" + ], + "acl":{ + "read":True, + "sub_acl":{ + "read":True + } + } + }, + { + "module":"asset", + "assets":[ + "$all" + ], + "acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True + } + }, + { + "module":"folder", + "folders":[ + "$all" + ], + "acl":{ + "read":True, + "sub_acl":{ + "read":True + } + } + }, + { + "module":"environment", + "environments":[ + "$all" + ], + "acl":{ + "read":True + } + }, + { + "module":"locale", + "locales":[ + "en-us" + ], + "acl":{ + "read":True + } + } + ] + } + } + + response = self.client.stack(api_key).roles().create(data) + self.assertEqual(response.request.url, f"{self.client.endpoint}roles") + self.assertEqual(response.status_code, 201) + + def test_update_roles(self): + data = data = { + "role":{ + "name":"sampleRole", + "description":"This is a test role.", + "rules":[ + { + "module":"branch", + "branches":[ + "main" + ], + "acl":{ + "read":True + } + }, + { + "module":"branch_alias", + "branch_aliases":[ + "deploy" + ], + "acl":{ + "read":True + } + }, + { + "module":"content_type", + "content_types":[ + "$all" + ], + "acl":{ + "read":True, + "sub_acl":{ + "read":True + } + } + }, + { + "module":"asset", + "assets":[ + "$all" + ], + "acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True + } + }, + { + "module":"folder", + "folders":[ + "$all" + ], + "acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True, + "sub_acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True + } + } + }, + { + "module":"environment", + "environments":[ + "$all" + ], + "acl":{ + "read":True + } + }, + { + "module":"locale", + "locales":[ + "$all" + ], + "acl":{ + "read":True + } + } + ], + "uid":"blt5a570885da41c710" + } + } + response = self.client.stack(api_key).roles(role_uid).update(data) + self.assertEqual(response.request.url, + f"{self.client.endpoint}roles/{role_uid}") + self.assertEqual(response.status_code, 200) + + + def test_delete_roles(self): + response = self.client.stack(api_key).roles(role_uid).delete() + self.assertEqual(response.status_code, 200) + + \ No newline at end of file diff --git a/tests/cred.py b/tests/cred.py index b777f74..6a95a61 100644 --- a/tests/cred.py +++ b/tests/cred.py @@ -27,6 +27,9 @@ default_folder_uid = "folder_uid" #Default folder uid default_workflow_uid = "workflow_uid" #Default workflow uid default_rule_uid = "rule_uid" #Default rule uid +default_metadata_uid = "metadata_uid" #Default metadata uid +default_role_uid = "roles_uid" #Default roles uid +default_log_item_uid = "log_item_uid" #Default roles uid def get_credentials(): @@ -59,7 +62,10 @@ def get_credentials(): "asset_uid": os.getenv("ASSET_UID", default_asset_uid), "folder_uid": os.getenv("FOLDER_UID", default_folder_uid), "workflow_uid": os.getenv("WORKFLOW_UID", default_workflow_uid), - "rule_uid": os.getenv("RULE_UID", default_rule_uid) + "rule_uid": os.getenv("RULE_UID", default_rule_uid), + "metadata_uid": os.getenv("METADATA_UID", default_metadata_uid), + "role_uid": os.getenv("ROLE_UID", default_role_uid), + "log_item_uid": os.getenv("LOG_ITEM_UID", default_log_item_uid) } return credentials diff --git a/tests/mock/assets/test_assets.py b/tests/mock/assets/test_assets_mock.py similarity index 100% rename from tests/mock/assets/test_assets.py rename to tests/mock/assets/test_assets_mock.py diff --git a/tests/mock/auditlogs/test_auditlogs_mock.py b/tests/mock/auditlogs/test_auditlogs_mock.py new file mode 100644 index 0000000..a5fffdb --- /dev/null +++ b/tests/mock/auditlogs/test_auditlogs_mock.py @@ -0,0 +1,46 @@ +import json +import os +import unittest + +from dotenv import load_dotenv + +from contentstack_management import contentstack +from tests.cred import get_credentials + +credentials = get_credentials() +username = credentials["username"] +password = credentials["password"] +api_key = credentials["api_key"] +host = credentials["host"] +log_item_uid = credentials["log_item_uid"] + + +class AuditlogMockTests(unittest.TestCase): + + def setUp(self): + + self.client = contentstack.ContentstackClient(host = host) + self.client.login(username, password) + + def read_file(self, file_name): + file_path= f"tests/resources/mock_auditlogs/{file_name}" + infile = open(file_path, 'r') + data = infile.read() + infile.close() + return data + + def test_mock_get_all_auditlogs(self): + response = self.client.stack(api_key).auditlog().find().json() + read_mock_auditlogs_data = self.read_file("find.json") + mock_auditlogs_data = json.loads(read_mock_auditlogs_data) + self.assertEqual(mock_auditlogs_data.keys(), response.keys()) + + def test_mock_get_a_auditlog(self): + response = self.client.stack(api_key).auditlog(log_item_uid).fetch().json() + read_mock_auditlogs_data = self.read_file("fetch.json") + mock_auditlogs_data = json.loads(read_mock_auditlogs_data) + self.assertEqual(mock_auditlogs_data.keys(), response.keys()) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/mock/metadata/test_metadata_mock.py b/tests/mock/metadata/test_metadata_mock.py new file mode 100644 index 0000000..5275d61 --- /dev/null +++ b/tests/mock/metadata/test_metadata_mock.py @@ -0,0 +1,120 @@ +import json +import os +import unittest + +from dotenv import load_dotenv +from contentstack_management import contentstack +from tests.cred import get_credentials + +credentials = get_credentials() +username = credentials["username"] +password = credentials["password"] +api_key = credentials["api_key"] +host = credentials["host"] +metadata_uid = credentials["metadata_uid"] + + +class metadataMockTests(unittest.TestCase): + + def setUp(self): + + self.client = contentstack.ContentstackClient(host = host) + self.client.login(username, password) + + + def read_file(self, file_name): + file_path= f"tests/resources/mock_metadata/{file_name}" + infile = open(file_path, 'r') + data = infile.read() + infile.close() + return data + + + def test_get_all_metadata(self): + response = self.client.stack(api_key).metadata().find().json() + read_mock_metadata_data = self.read_file("find.json") + mock_metadata_data = json.loads(read_mock_metadata_data) + self.assertEqual(mock_metadata_data.keys(), response.keys()) + + def test_get_a_metadata(self): + response = self.client.stack(api_key).metadata(metadata_uid).fetch().json() + read_mock_metadata_data = self.read_file("fetch.json") + mock_metadata_data = json.loads(read_mock_metadata_data) + self.assertEqual(mock_metadata_data.keys(), response.keys()) + + def test_create(self): + data = { + "metadata": { + "entity_uid": "entry_uid", + "type": "entry", + "_content_type_uid": "_content_type_uid", + "extension_uid": "extension_uid", + "presets": [{ + "uid": "presents_uid", + "name": "Test1", + "options": { + + } + }] + } + } + response = self.client.stack(api_key).metadata().create(data).json() + read_mock_metadata_data = self.read_file("create.json") + mock_metadata_data = json.loads(read_mock_metadata_data) + self.assertEqual(mock_metadata_data.keys(), response.keys()) + + def test_update_metadata(self): + data = { + "metadata": { + "entity_uid": "entry_uid", + "type": "entry", + "_content_type_uid": "_content_type_uid", + "extension_uid": "extension_uid", + "presets": [{ + "uid": "presents_uid", + "name": "Test1", + "options": { + + } + }] + } + } + response = self.client.stack(api_key).metadata(metadata_uid).update(data).json() + read_mock_metadata_data = self.read_file("update.json") + mock_metadata_data = json.loads(read_mock_metadata_data) + self.assertEqual(mock_metadata_data.keys(), response.keys()) + + def test_delete_metadata(self): + response = self.client.stack(api_key).metadata(metadata_uid).delete().json() + read_mock_metadata_data = self.read_file("delete.json") + mock_metadata_data = json.loads(read_mock_metadata_data) + self.assertEqual(mock_metadata_data['notice'], response['notice']) + + + def test_publish(self): + data = { + "metadata": { + "environments": [ + "environment_name" + ], + "locales": [ + "en-us" + ] + } + } + response = self.client.stack(api_key).metadata(metadata_uid).publish(data).json() + self.assertEqual("Metadata sent for publishing.", response['notice']) + + def test_unpublish(self): + data = { + "metadata": { + "environments": [ + "environment_name" + ], + "locales": [ + "en-us" + ] + } + } + response = self.client.stack(api_key).metadata(metadata_uid).unpublish(data).json() + self.assertEqual("Metadata sent for unpublishing.", response['notice']) diff --git a/tests/mock/roles/test_roles_mock.py b/tests/mock/roles/test_roles_mock.py new file mode 100644 index 0000000..7d5e0f5 --- /dev/null +++ b/tests/mock/roles/test_roles_mock.py @@ -0,0 +1,230 @@ +import json +import os +import unittest + +from dotenv import load_dotenv +from contentstack_management import contentstack +from tests.cred import get_credentials + +credentials = get_credentials() +username = credentials["username"] +password = credentials["password"] +api_key = credentials["api_key"] +host = credentials["host"] +role_uid = credentials["role_uid"] + + +class rolesMockTests(unittest.TestCase): + + def setUp(self): + + self.client = contentstack.ContentstackClient(host = host) + self.client.login(username, password) + + + def read_file(self, file_name): + file_path= f"tests/resources/mock_roles/{file_name}" + infile = open(file_path, 'r') + data = infile.read() + infile.close() + return data + + + def test_get_all_roles(self): + response = self.client.stack(api_key).roles().find().json() + read_mock_roles_data = self.read_file("find.json") + mock_roles_data = json.loads(read_mock_roles_data) + self.assertEqual(mock_roles_data.keys(), response.keys()) + + def test_get_a_roles(self): + response = self.client.stack(api_key).roles(role_uid).fetch().json() + read_mock_roles_data = self.read_file("fetch.json") + mock_roles_data = json.loads(read_mock_roles_data) + self.assertEqual(mock_roles_data.keys(), response.keys()) + + def test_create(self): + data = { + "role":{ + "name":"testRole", + "description":"This is a test role.", + "rules":[ + { + "module":"branch", + "branches":[ + "main" + ], + "acl":{ + "read":True + } + }, + { + "module":"branch_alias", + "branch_aliases":[ + "deploy" + ], + "acl":{ + "read":True + } + }, + { + "module":"content_type", + "content_types":[ + "$all" + ], + "acl":{ + "read":True, + "sub_acl":{ + "read":True + } + } + }, + { + "module":"asset", + "assets":[ + "$all" + ], + "acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True + } + }, + { + "module":"folder", + "folders":[ + "$all" + ], + "acl":{ + "read":True, + "sub_acl":{ + "read":True + } + } + }, + { + "module":"environment", + "environments":[ + "$all" + ], + "acl":{ + "read":True + } + }, + { + "module":"locale", + "locales":[ + "en-us" + ], + "acl":{ + "read":True + } + } + ] + } + } + + response = self.client.stack(api_key).roles().create(data).json() + read_mock_roles_data = self.read_file("create.json") + mock_roles_data = json.loads(read_mock_roles_data) + self.assertEqual(mock_roles_data.keys(), response.keys()) + + def test_update_roles(self): + data = { + "role":{ + "name":"sampleRole", + "description":"This is a test role.", + "rules":[ + { + "module":"branch", + "branches":[ + "main" + ], + "acl":{ + "read":True + } + }, + { + "module":"branch_alias", + "branch_aliases":[ + "deploy" + ], + "acl":{ + "read":True + } + }, + { + "module":"content_type", + "content_types":[ + "$all" + ], + "acl":{ + "read":True, + "sub_acl":{ + "read":True + } + } + }, + { + "module":"asset", + "assets":[ + "$all" + ], + "acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True + } + }, + { + "module":"folder", + "folders":[ + "$all" + ], + "acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True, + "sub_acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True + } + } + }, + { + "module":"environment", + "environments":[ + "$all" + ], + "acl":{ + "read":True + } + }, + { + "module":"locale", + "locales":[ + "$all" + ], + "acl":{ + "read":True + } + } + ], + "uid":"blt5a570885da41c710" + } + } + response = self.client.stack(api_key).roles(role_uid).update(data).json() + read_mock_roles_data = self.read_file("update.json") + mock_roles_data = json.loads(read_mock_roles_data) + self.assertEqual(mock_roles_data.keys(), response.keys()) + + def test_delete_roles(self): + response = self.client.stack(api_key).roles(role_uid).delete().json() + read_mock_roles_data = self.read_file("delete.json") + mock_roles_data = json.loads(read_mock_roles_data) + self.assertEqual(mock_roles_data['notice'], response['notice']) + diff --git a/tests/resources/mock_auditlogs/fetch.json b/tests/resources/mock_auditlogs/fetch.json new file mode 100644 index 0000000..10f60e0 --- /dev/null +++ b/tests/resources/mock_auditlogs/fetch.json @@ -0,0 +1,81 @@ +{ + "log": { + "_id": "id", + "uid": "log_uid", + "stack": "stack_uid", + "created_at": "2023-08-17T21:53:29.817Z", + "created_by": "user_uid", + "module": "global_field", + "event_type": "update", + "request_id": "request_id", + "metadata": { + "title": "First", + "uid": "first", + "version": 6 + }, + "remote_addr": "ip_address", + "request": { + "global_field": { + "title": "First", + "description": "", + "schema": [ + { + "display_name": "Name", + "uid": "name", + "data_type": "text", + "multiple": false, + "mandatory": false, + "unique": false, + "non_localizable": false + }, + { + "data_type": "text", + "display_name": "Rich text editor", + "uid": "description", + "multiple": false, + "mandatory": false, + "unique": false, + "non_localizable": false + } + ] + } + }, + "response": { + "notice": "Global Field updated successfully.", + "global_field": { + "created_at": "2023-07-13T08:14:10.772Z", + "updated_at": "2023-08-17T21:53:29.794Z", + "title": "First", + "uid": "first", + "_version": 6, + "inbuilt_class": false, + "schema": [ + { + "display_name": "Name", + "uid": "name", + "data_type": "text", + "multiple": false, + "mandatory": false, + "unique": false, + "non_localizable": false + }, + { + "data_type": "text", + "display_name": "Rich text editor", + "uid": "description", + "multiple": false, + "mandatory": false, + "unique": false, + "non_localizable": false + } + ], + "last_activity": {}, + "maintain_revisions": true, + "description": "", + "DEFAULT_ACL": null, + "SYS_ACL": null, + "field_rules": null + } + } + } + } \ No newline at end of file diff --git a/tests/resources/mock_auditlogs/find.json b/tests/resources/mock_auditlogs/find.json new file mode 100644 index 0000000..de88e22 --- /dev/null +++ b/tests/resources/mock_auditlogs/find.json @@ -0,0 +1,321 @@ +{ + "logs": [ + { + "uid": "uid", + "stack": "stack", + "created_at": "2023-08-17T21:53:29.817Z", + "created_by": "created_by", + "module": "global_field", + "event_type": "update", + "request_id": "request_id", + "metadata": { + "title": "First", + "uid": "first", + "version": 6 + }, + "remote_addr": "ip_addr", + "request": { + "global_field": { + "title": "First", + "description": "", + "schema": [ + { + "display_name": "Name", + "uid": "name", + "data_type": "text", + "multiple": false, + "mandatory": false, + "unique": false, + "non_localizable": false + }, + { + "data_type": "text", + "display_name": "Rich text editor", + "uid": "description", + "multiple": false, + "mandatory": false, + "unique": false, + "non_localizable": false + } + ] + } + }, + "response": { + "notice": "Global Field updated successfully.", + "global_field": { + "created_at": "2023-07-13T08:14:10.772Z", + "updated_at": "2023-08-17T21:53:29.794Z", + "title": "First", + "uid": "first", + "_version": 6, + "inbuilt_class": false, + "schema": [ + { + "display_name": "Name", + "uid": "name", + "data_type": "text", + "multiple": false, + "mandatory": false, + "unique": false, + "non_localizable": false + }, + { + "data_type": "text", + "display_name": "Rich text editor", + "uid": "description", + "multiple": false, + "mandatory": false, + "unique": false, + "non_localizable": false + } + ], + "last_activity": {}, + "maintain_revisions": true, + "description": "", + "DEFAULT_ACL": null, + "SYS_ACL": null, + "field_rules": null + } + } + }, + { + "stack": "stack", + "created_by": "created_by", + "created_at": "2023-08-16T17:00:18.962Z", + "request_id": "request_id", + "event_type": "create", + "module": "webhook", + "metadata": { + "title": "Test", + "uid": "uid", + "version": 1 + }, + "remote_addr": "ip_addr", + "request": { + "webhook": { + "name": "Test", + "destinations": [ + { + "target_url": "http://example.com", + "http_basic_auth": "basic", + "http_basic_password": "test", + "custom_header": [ + { + "header_name": "Custom", + "value": "testing" + } + ] + } + ], + "notifiers": "dave.joe@gmail.com", + "channels": [ + "assets.create" + ], + "branches": [ + "main" + ], + "retry_policy": "manual", + "disabled": false, + "concise_payload": true, + "org_uid": "org_uid", + "applikation_id": "applikation_id", + "updated_by": "updated_by", + "created_by": "created_by", + "project_uid": "project_uid" + } + }, + "response": { + "notice": "The Webhook was created successfully", + "webhook": { + "name": "Test", + "destinations": [ + { + "target_url": "http://example.com", + "http_basic_auth": "basic", + "http_basic_password": "test", + "custom_header": [ + { + "header_name": "Custom", + "value": "testing" + } + ] + } + ], + "channels": [ + "assets.create" + ], + "retry_policy": "manual", + "branches": [ + "main" + ], + "notifiers": [ + "dave.joe@gmail.com" + ], + "disabled": false, + "updated_by": "updated_by", + "created_by": "created_by", + "concise_payload": true, + "uid": "uid", + "created_at": "2023-08-16T17:00:18.956Z", + "updated_at": "2023-08-16T17:00:18.956Z" + } + }, + "uid": "uid", + "__v": 0 + }, + { + "uid": "uid", + "stack": "stack", + "created_at": "2023-08-09T09:46:42.344Z", + "created_by": "created_by", + "module": "entry/asset", + "event_type": "bulk-publish", + "request_id": "request_id", + "metadata": { + "title": "Bulk Publish", + "uid": "" + }, + "remote_addr": "remote_addr", + "request": { + "r": "0.15904304909842337", + "locales": [ + "en-at", + "en-us", + "hi-in" + ], + "environments": [ + "environments" + ], + "assets": [ + { + "uid": "uid", + "version": 1, + "parent_uid": null, + "content_type": "image/jpeg", + "file_size": 131268, + "filename": "dosa.jpeg", + "dimension": { + "height": 696, + "width": 696 + }, + "asset_id": "asset_id", + "title": "dosa.jpeg", + "_version": 1, + "_branches": [ + "main" + ], + "type": "asset", + "entry": { + "title": "dosa.jpeg", + "uid": "uid", + "version": 1 + }, + "environments": [ + "environments" + ], + "locales": [ + "en-at", + "en-us", + "hi-in" + ] + }, + { + "uid": "uid", + "version": 1, + "parent_uid": null, + "content_type": "image/png", + "file_size": 108095, + "filename": "light_house_reports.png", + "dimension": { + "height": 909, + "width": 1920 + }, + "asset_id": "asset_id", + "title": "light_house_reports.png", + "_version": 1, + "_branches": [ + "main", + "release", + "release2" + ], + "type": "asset", + "entry": { + "title": "light_house_reports.png", + "uid": "uid", + "version": 1 + }, + "environments": [ + "environments" + ], + "locales": [ + "en-at", + "en-us", + "hi-in" + ] + }, + { + "uid": "uid", + "version": 1, + "content_type": "multipart/form-data", + "file_size": 330000, + "filename": "shailesh", + "asset_id": "asset_id", + "title": "shailesh", + "_version": 1, + "parent_uid": null, + "_branches": [ + "main", + "release2" + ], + "type": "asset", + "entry": { + "title": "shailesh", + "uid": "uid", + "version": 1 + }, + "environments": [ + "environments" + ], + "locales": [ + "en-at", + "en-us", + "hi-in" + ] + }, + { + "uid": "uid", + "version": 1, + "content_type": "application/json", + "file_size": 392, + "filename": "webhook.json", + "asset_id": "asset_id", + "title": "webhook.json", + "_version": 1, + "parent_uid": null, + "_branches": [ + "main", + "release", + "release2" + ], + "type": "asset", + "entry": { + "title": "webhook.json", + "uid": "uid", + "version": 1 + }, + "environments": [ + "environments" + ], + "locales": [ + "en-at", + "en-us", + "hi-in" + ] + } + ], + "entries": [] + }, + "response": {} + } + ] + } \ No newline at end of file diff --git a/tests/resources/mock_metadata/create.json b/tests/resources/mock_metadata/create.json new file mode 100644 index 0000000..a58687a --- /dev/null +++ b/tests/resources/mock_metadata/create.json @@ -0,0 +1,28 @@ +{ + "notice":"Metadata created successfully.", + "metadata":{ + "entity_uid":"entity_uid", + "type":"entry", + "_content_type_uid":"_content_type_uid", + "extension_uid":"extension_uid", + "presets":[ + { + "uid":"presets_uid", + "name":"Test1", + "options":{ + + } + } + ], + "uid":"uid", + "locale":"en-us", + "scope":"local", + "created_by":"user_uid", + "updated_by":"user_uid", + "created_at":"2023-08-08T16:35:02.599Z", + "updated_at":"2023-08-08T16:35:02.599Z", + "api_key":"api_key", + "deleted_at":false, + "_version":1 + } + } \ No newline at end of file diff --git a/tests/resources/mock_metadata/delete.json b/tests/resources/mock_metadata/delete.json new file mode 100644 index 0000000..b5fe4a6 --- /dev/null +++ b/tests/resources/mock_metadata/delete.json @@ -0,0 +1,3 @@ +{ + "notice":"Metadata deleted successfully." + } \ No newline at end of file diff --git a/tests/resources/mock_metadata/fetch.json b/tests/resources/mock_metadata/fetch.json new file mode 100644 index 0000000..ebcbeeb --- /dev/null +++ b/tests/resources/mock_metadata/fetch.json @@ -0,0 +1,27 @@ +{ + "metadata":{ + "uid":"uid", + "extension_uid":"extension_uid", + "type":"entry", + "entity_uid":"entity_uid", + "_content_type_uid":"_content_type_uid", + "locale":"en-us", + "api_key":"api_key", + "scope":"local", + "created_by":"user_uid", + "updated_by":"user_uid", + "created_at":"2023-08-08T16:35:02.599Z", + "updated_at":"2023-08-08T16:38:25.150Z", + "deleted_at":false, + "_version":2, + "presets":[ + { + "uid":"presents_uid", + "name":"Test1", + "options":{ + + } + } + ] + } + } \ No newline at end of file diff --git a/tests/resources/mock_metadata/find.json b/tests/resources/mock_metadata/find.json new file mode 100644 index 0000000..ac99fac --- /dev/null +++ b/tests/resources/mock_metadata/find.json @@ -0,0 +1,29 @@ +{ + "metadata":[ + { + "uid":"uid", + "extension_uid":"extension_uid", + "type":"entry", + "entity_uid":"entity_uid", + "_content_type_uid":"_content_type_uid", + "locale":"en-us", + "api_key":"api_key", + "scope":"local", + "created_by":"user_uid", + "updated_by":"user_uid", + "created_at":"2023-08-08T16:35:02.599Z", + "updated_at":"2023-08-08T16:38:25.150Z", + "deleted_at":false, + "_version":2, + "presets":[ + { + "uid":"presents_uid", + "name":"Test1", + "options":{ + + } + } + ] + } + ] + } \ No newline at end of file diff --git a/tests/resources/mock_metadata/update.json b/tests/resources/mock_metadata/update.json new file mode 100644 index 0000000..dfdd300 --- /dev/null +++ b/tests/resources/mock_metadata/update.json @@ -0,0 +1,28 @@ +{ + "notice":"Metadata updated successfully.", + "metadata":{ + "uid":"uid", + "extension_uid":"extension_uid", + "type":"entry", + "entity_uid":"entity_uid", + "_content_type_uid":"_content_type_uid", + "locale":"en-us", + "api_key":"api_key", + "scope":"local", + "created_by":"user_uid", + "updated_by":"user_uid", + "created_at":"2023-08-08T16:35:02.599Z", + "updated_at":"2023-08-08T16:38:25.150Z", + "deleted_at":false, + "_version":2, + "presets":[ + { + "uid":"presents_uid", + "name":"Test1", + "options":{ + + } + } + ] + } + } \ No newline at end of file diff --git a/tests/resources/mock_roles/create.json b/tests/resources/mock_roles/create.json new file mode 100644 index 0000000..74ce949 --- /dev/null +++ b/tests/resources/mock_roles/create.json @@ -0,0 +1,89 @@ +{ + "notice": "The role created successfully.", + "role": { + "name": "testRole", + "description": "This is a test role.", + "rules": [ + { + "module": "branch", + "branches": [ + "main" + ], + "acl": { + "read": true + } + }, + { + "module": "branch_alias", + "branch_aliases": [ + "branch_aliases_uid" + ], + "acl": { + "read": true + } + }, + { + "module": "content_type", + "content_types": [ + "$all" + ], + "acl": { + "read": true, + "sub_acl": { + "read": true + } + } + }, + { + "module": "asset", + "assets": [ + "$all" + ], + "acl": { + "read": true, + "update": true, + "publish": true, + "delete": true + } + }, + { + "module": "folder", + "folders": [ + "$all" + ], + "acl": { + "read": true, + "sub_acl": { + "read": true + } + } + }, + { + "module": "environment", + "environments": [ + "$all" + ], + "acl": { + "read": true + } + }, + { + "module": "locale", + "locales": [ + "en-us" + ], + "acl": { + "read": true + } + } + ], + "users": [], + "uid": "role_uid", + "org_uid": "org_uid", + "api_key": "api_key", + "created_by": "user_uid", + "updated_by": "user_uid", + "created_at": "2023-08-16T11:25:55.953Z", + "updated_at": "2023-08-16T11:25:55.953Z" + } + } \ No newline at end of file diff --git a/tests/resources/mock_roles/delete.json b/tests/resources/mock_roles/delete.json new file mode 100644 index 0000000..c241a5b --- /dev/null +++ b/tests/resources/mock_roles/delete.json @@ -0,0 +1,3 @@ +{ + "notice": "The role deleted successfully." + } \ No newline at end of file diff --git a/tests/resources/mock_roles/fetch.json b/tests/resources/mock_roles/fetch.json new file mode 100644 index 0000000..4fd05da --- /dev/null +++ b/tests/resources/mock_roles/fetch.json @@ -0,0 +1,57 @@ +{ + "role": { + "name": "Content Manager", + "description": "Content Managers can view all content types, manage entries and assets. They cannot edit content types or access stack settings.", + "uid": "role_uid", + "created_by": "user_uid", + "updated_by": "user_uids", + "created_at": "2023-05-15T09:52:13.042Z", + "updated_at": "2023-07-24T12:06:47.704Z", + "users": [ + "user1_uid", + "user2_uid", + "user3_uid" + ], + "roles": [], + "owner": "owner@contentstack.com", + "stack": { + "created_at": "2023-05-15T09:52:12.429Z", + "updated_at": "2023-08-04T12:38:49.775Z", + "uid": "stack_uid", + "name": "stack_name", + "org_uid": "org_uid", + "api_key": "api_key", + "master_locale": "en-us", + "is_asset_download_public": true, + "owner_uid": "owner_uid", + "user_uids": [ + "user1_uid", + "user2_uid", + "user3_uid", + "user4_uid" + ], + "settings": { + "version": "2019-04-30", + "rte_version": 3, + "blockAuthQueryParams": true, + "allowedCDNTokens": [ + "access_token" + ], + "branches": true, + "localesOptimization": false, + "webhook_enabled": true, + "stack_variables": {}, + "live_preview": {}, + "discrete_variables": { + "cms": true, + "_version": 3, + "secret_key": "secret_key" + }, + "language_fallback": false, + "rte": {}, + "workflow_stages": true + } + }, + "SYS_ACL": {} + } + } \ No newline at end of file diff --git a/tests/resources/mock_roles/find.json b/tests/resources/mock_roles/find.json new file mode 100644 index 0000000..f94af9c --- /dev/null +++ b/tests/resources/mock_roles/find.json @@ -0,0 +1,108 @@ +{ + "roles": [ + { + "name": "Developer", + "description": "Developer can perform all Content Manager's actions, view audit logs, create roles, invite users, manage content types, languages, and environments.", + "uid": "role_uid", + "created_by": "user_uid", + "updated_by": "user_uid", + "created_at": "2023-05-15T09:52:13.042Z", + "updated_at": "2023-07-24T12:06:47.704Z", + "users": [ + "user1_uid", + "user2_uid", + "user3_uid" + ], + "roles": [], + "owner": "user@contentstack.com", + "stack": { + "created_at": "2023-05-15T09:52:12.429Z", + "updated_at": "2023-08-04T12:38:49.775Z", + "uid": "stack_uid", + "name": "TestSDK", + "org_uid": "org_uid", + "api_key": "api_key", + "master_locale": "en-us", + "is_asset_download_public": true, + "owner_uid": "owner_uid", + "user_uids": [ + "user1_uid", + "user2_uid", + "user3_uid", + "user4_uid" + ], + "settings": { + "version": "2019-04-30", + "rte_version": 3, + "blockAuthQueryParams": true, + "allowedCDNTokens": [ + "access_token" + ], + "branches": true, + "localesOptimization": false, + "webhook_enabled": true, + "stack_variables": {}, + "live_preview": {}, + "discrete_variables": { + "cms": true, + "_version": 3, + "secret_key": "secret_key" + }, + "language_fallback": false, + "rte": {}, + "workflow_stages": true + } + }, + "SYS_ACL": {} + }, + { + "name": "Admin", + "description": "Admin can perform all actions and manage all settings of the stack, except the ability to delete or transfer ownership of the stack.", + "uid": "role_uid", + "created_by": "user_uid", + "updated_by": "user_uid", + "created_at": "2023-05-15T09:52:13.042Z", + "updated_at": "2023-05-15T09:52:13.042Z", + "owner": "user@contentstack.com", + "stack": { + "created_at": "2023-05-15T09:52:12.429Z", + "updated_at": "2023-08-04T12:38:49.775Z", + "uid": "stack_uid", + "name": "TestSDK", + "org_uid": "org_uid", + "api_key": "api_key", + "master_locale": "en-us", + "is_asset_download_public": true, + "owner_uid": "owner_uid", + "user_uids": [ + "user1_uid", + "user2_uid", + "user3_uid", + "user4_uid" + ], + "settings": { + "version": "2019-04-30", + "rte_version": 3, + "blockAuthQueryParams": true, + "allowedCDNTokens": [ + "access_token" + ], + "branches": true, + "localesOptimization": false, + "webhook_enabled": true, + "stack_variables": {}, + "live_preview": {}, + "discrete_variables": { + "cms": true, + "_version": 3, + "secret_key": "secret_key" + }, + "language_fallback": false, + "rte": {}, + "workflow_stages": true + } + }, + "SYS_ACL": {} + } + ] + } \ No newline at end of file diff --git a/tests/resources/mock_roles/update.json b/tests/resources/mock_roles/update.json new file mode 100644 index 0000000..7cf225d --- /dev/null +++ b/tests/resources/mock_roles/update.json @@ -0,0 +1,95 @@ +{ + "notice": "The role updated successfully.", + "role": { + "name": "sampleRole", + "description": "This is a test role.", + "rules": [ + { + "module": "branch", + "branches": [ + "main" + ], + "acl": { + "read": true + } + }, + { + "module": "branch_alias", + "branch_aliases": [ + "branch_aliases_uid" + ], + "acl": { + "read": true + } + }, + { + "module": "content_type", + "content_types": [ + "$all" + ], + "acl": { + "read": true, + "sub_acl": { + "read": true + } + } + }, + { + "module": "asset", + "assets": [ + "$all" + ], + "acl": { + "read": true, + "update": true, + "publish": true, + "delete": true + } + }, + { + "module": "folder", + "folders": [ + "$all" + ], + "acl": { + "read": true, + "update": true, + "publish": true, + "delete": true, + "sub_acl": { + "read": true, + "update": true, + "publish": true, + "delete": true + } + } + }, + { + "module": "environment", + "environments": [ + "$all" + ], + "acl": { + "read": true + } + }, + { + "module": "locale", + "locales": [ + "$all" + ], + "acl": { + "read": true + } + } + ], + "users": [], + "uid": "role_uid", + "org_uid": "org_uid", + "api_key": "api_key", + "created_by": "user_uid", + "updated_by": "user_uid", + "created_at": "2023-08-16T11:25:55.953Z", + "updated_at": "2023-08-16T11:28:13.724Z" + } + } \ No newline at end of file diff --git a/tests/unit/assets/test_assets.py b/tests/unit/assets/test_assets_unit.py similarity index 97% rename from tests/unit/assets/test_assets.py rename to tests/unit/assets/test_assets_unit.py index d51a7db..00986fa 100644 --- a/tests/unit/assets/test_assets.py +++ b/tests/unit/assets/test_assets_unit.py @@ -198,20 +198,16 @@ def test_get_folder(self): self.assertEqual(response.request.body, None) def test_get_folder_by_name(self): - query = {"is_dir": True, "name": "folder_name"} - q = json.dumps((query)) response = self.client.stack().assets().folder_by_name() p=print(response.request.url) - self.assertEqual(response.request.url, f"{self.client.endpoint}assets?include_folders=true&query={q}&folder={folder_uid}") + self.assertEqual(response.request.url, f"{self.client.endpoint}assets?query=is_dir&query=name") self.assertEqual(response.request.method, "GET") self.assertEqual(response.request.headers["Content-Type"], "application/json") self.assertEqual(response.request.body, None) def test_get_subfolders(self): - query = {"is_dir": True} - q = str(query) - response = self.client.stack().assets().get_subfolders(folder_uid, query) - self.assertEqual(response.request.url, f"{self.client.endpoint}assets?include_folders=true&query={q}&folder={folder_uid}") + response = self.client.stack().assets().get_subfolders(folder_uid) + self.assertEqual(response.request.url, f"{self.client.endpoint}assets?include_folders=True&query=is_dir&folder={folder_uid}") self.assertEqual(response.request.method, "GET") self.assertEqual(response.request.headers["Content-Type"], "application/json") self.assertEqual(response.request.body, None) diff --git a/tests/unit/auditlogs/test_auditlog_unit.py b/tests/unit/auditlogs/test_auditlog_unit.py new file mode 100644 index 0000000..6b51c0f --- /dev/null +++ b/tests/unit/auditlogs/test_auditlog_unit.py @@ -0,0 +1,65 @@ +import os +import unittest + +from dotenv import load_dotenv + +from contentstack_management import contentstack +from contentstack_management._errors import ArgumentException +from tests.cred import get_credentials + +credentials = get_credentials() +username = credentials["username"] +password = credentials["password"] +host = credentials["host"] +api_key = credentials["api_key"] +log_item_uid = credentials["log_item_uid"] + + +class auditlogesUnitTests(unittest.TestCase): + + def setUp(self): + self.client = contentstack.ContentstackClient(host=host) + self.client.login(username, password) + + def test_get_all_auditloges(self): + response = self.client.stack(api_key).auditlog().find() + self.assertEqual(response.request.url, f"{self.client.endpoint}audit-logs") + self.assertEqual(response.request.method, "GET") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + self.assertEqual(response.request.body, None) + + def test_get_a_auditlog(self): + response = self.client.stack(api_key).auditlog(log_item_uid).fetch() + self.assertEqual(response.request.url, f"{self.client.endpoint}audit-logs/{log_item_uid}") + self.assertEqual(response.request.method, "GET") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + self.assertEqual(response.request.body, None) + + def test_get_a_auditlog_invalid_input(self): + try: + response = self.client.stack(api_key).auditlog().fetch() + except ArgumentException as e: + if hasattr(e, 'message'): + self.assertEqual( + "Log item Uid is required", e.args[0]) + + def test_get_all_auditloges_with_params(self): + query = self.client.stack(api_key).auditlog() + query.add_param("include_branch", True) + response = query.find() + self.assertEqual(response.request.url, f"{self.client.endpoint}audit-logs") + self.assertEqual(response.request.method, "GET") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + self.assertEqual(response.request.body, None) + + def test_get_a_auditlog_with_params(self): + query = self.client.stack(api_key).auditlog(log_item_uid) + query.add_param("include_branch", False) + response = query.fetch() + self.assertEqual(response.request.url, f"{self.client.endpoint}audit-logs/{log_item_uid}") + self.assertEqual(response.request.method, "GET") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + self.assertEqual(response.request.body, None) + + # if __name__ == '__main__': + # unittest.main() \ No newline at end of file diff --git a/tests/unit/metadata/test_metadata_unit.py b/tests/unit/metadata/test_metadata_unit.py new file mode 100644 index 0000000..2cc3fe0 --- /dev/null +++ b/tests/unit/metadata/test_metadata_unit.py @@ -0,0 +1,118 @@ +import os +import unittest +from dotenv import load_dotenv +from contentstack_management import contentstack +from tests.cred import get_credentials + +credentials = get_credentials() +username = credentials["username"] +password = credentials["password"] +host = credentials["host"] +api_key = credentials["api_key"] +metadata_uid = credentials["metadata_uid"] + +class MetadataUnitTests(unittest.TestCase): + + def setUp(self): + self.client = contentstack.ContentstackClient(host=host) + self.client.login(username, password) + + def test_get_all_metadata(self): + response = self.client.stack(api_key).metadata().find() + self.assertEqual(response.request.url, f"{self.client.endpoint}metadata") + self.assertEqual(response.request.method, "GET") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + self.assertEqual(response.request.body, None) + + def test_get_a_metadata(self): + response = self.client.stack(api_key).metadata(metadata_uid).fetch() + self.assertEqual(response.request.url, + f"{self.client.endpoint}metadata/{metadata_uid}") + self.assertEqual(response.request.method, "GET") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + self.assertEqual(response.request.body, None) + + def test_create(self): + data = { + "metadata": { + "entity_uid": "entity_uid", + "type": "entry", + "_content_type_uid": "_content_type_uid", + "extension_uid": "extension_uid", + "presets": [{ + "uid": "presents_uid", + "name": "Test1", + "options": { + + } + }] + } + } + response = self.client.stack(api_key).metadata().create(data) + self.assertEqual(response.request.url, f"{self.client.endpoint}metadata") + self.assertEqual(response.request.method, "POST") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + + def test_update_metadata(self): + data = { + "metadata": { + "entity_uid": "entity_uid", + "type": "entry", + "_content_type_uid": "_content_type_uid", + "extension_uid": "extension_uid", + "presets": [{ + "uid": "presents_uid", + "name": "Test1", + "options": { + + } + }] + } + } + response = self.client.stack(api_key).metadata(metadata_uid).update(data) + self.assertEqual(response.request.url, + f"{self.client.endpoint}metadata/{metadata_uid}") + self.assertEqual(response.request.method, "PUT") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + + + def test_delete_metadata(self): + response = self.client.stack(api_key).metadata(metadata_uid).delete() + self.assertEqual(response.request.url, f"{self.client.endpoint}metadata/{metadata_uid}") + self.assertEqual(response.request.method, "DELETE") + + def test_publish(self): + data = { + "metadata": { + "environments": [ + "environment_name" + ], + "locales": [ + "en-us" + ] + } + } + response = self.client.stack(api_key).metadata(metadata_uid).publish(data) + self.assertEqual(response.request.url, + f"{self.client.endpoint}metadata/{metadata_uid}/publish") + self.assertEqual(response.request.method, "POST") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + + def test_unpublish(self): + data = { + "metadata": { + "environments": [ + "environment_name" + ], + "locales": [ + "en-us" + ] + } + } + response = self.client.stack(api_key).metadata(metadata_uid).unpublish(data) + self.assertEqual(response.request.url, + f"{self.client.endpoint}metadata/{metadata_uid}/unpublish") + self.assertEqual(response.request.method, "POST") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + + \ No newline at end of file diff --git a/tests/unit/roles/test_roles_unit.py b/tests/unit/roles/test_roles_unit.py new file mode 100644 index 0000000..30b36d2 --- /dev/null +++ b/tests/unit/roles/test_roles_unit.py @@ -0,0 +1,220 @@ +import os +import unittest +from dotenv import load_dotenv +from contentstack_management import contentstack +from tests.cred import get_credentials + +credentials = get_credentials() +username = credentials["username"] +password = credentials["password"] +host = credentials["host"] +api_key = credentials["api_key"] +role_uid = credentials["role_uid"] + +class rolesUnitTests(unittest.TestCase): + + def setUp(self): + self.client = contentstack.ContentstackClient(host=host) + self.client.login(username, password) + + def test_get_all_roles(self): + response = self.client.stack(api_key).roles().find() + self.assertEqual(response.request.url, f"{self.client.endpoint}roles") + self.assertEqual(response.request.method, "GET") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + self.assertEqual(response.request.body, None) + + def test_get_a_roles(self): + response = self.client.stack(api_key).roles(role_uid).fetch() + self.assertEqual(response.request.url, + f"{self.client.endpoint}roles/{role_uid}") + self.assertEqual(response.request.method, "GET") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + self.assertEqual(response.request.body, None) + + def test_create(self): + data = { + "role":{ + "name":"testRole", + "description":"This is a test role.", + "rules":[ + { + "module":"branch", + "branches":[ + "main" + ], + "acl":{ + "read":True + } + }, + { + "module":"branch_alias", + "branch_aliases":[ + "deploy" + ], + "acl":{ + "read":True + } + }, + { + "module":"content_type", + "content_types":[ + "$all" + ], + "acl":{ + "read":True, + "sub_acl":{ + "read":True + } + } + }, + { + "module":"asset", + "assets":[ + "$all" + ], + "acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True + } + }, + { + "module":"folder", + "folders":[ + "$all" + ], + "acl":{ + "read":True, + "sub_acl":{ + "read":True + } + } + }, + { + "module":"environment", + "environments":[ + "$all" + ], + "acl":{ + "read":True + } + }, + { + "module":"locale", + "locales":[ + "en-us" + ], + "acl":{ + "read":True + } + } + ] + } + } + + response = self.client.stack(api_key).roles().create(data) + self.assertEqual(response.request.url, f"{self.client.endpoint}roles") + self.assertEqual(response.request.method, "POST") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + + def test_update_roles(self): + data = { + "role":{ + "name":"sampleRole", + "description":"This is a test role.", + "rules":[ + { + "module":"branch", + "branches":[ + "main" + ], + "acl":{ + "read":True + } + }, + { + "module":"branch_alias", + "branch_aliases":[ + "deploy" + ], + "acl":{ + "read":True + } + }, + { + "module":"content_type", + "content_types":[ + "$all" + ], + "acl":{ + "read":True, + "sub_acl":{ + "read":True + } + } + }, + { + "module":"asset", + "assets":[ + "$all" + ], + "acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True + } + }, + { + "module":"folder", + "folders":[ + "$all" + ], + "acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True, + "sub_acl":{ + "read":True, + "update":True, + "publish":True, + "delete":True + } + } + }, + { + "module":"environment", + "environments":[ + "$all" + ], + "acl":{ + "read":True + } + }, + { + "module":"locale", + "locales":[ + "$all" + ], + "acl":{ + "read":True + } + } + ], + "uid":"role_uid" + } + } + response = self.client.stack(api_key).roles(role_uid).update(data) + self.assertEqual(response.request.url, + f"{self.client.endpoint}roles/{role_uid}") + self.assertEqual(response.request.method, "PUT") + self.assertEqual(response.request.headers["Content-Type"], "application/json") + + + def test_delete_roles(self): + response = self.client.stack(api_key).roles(role_uid).delete() + self.assertEqual(response.request.url, f"{self.client.endpoint}roles/{role_uid}") + self.assertEqual(response.request.method, "DELETE") diff --git a/tests/unit/stack/test_stack.py b/tests/unit/stack/test_stack.py index 18d797d..67d55d5 100644 --- a/tests/unit/stack/test_stack.py +++ b/tests/unit/stack/test_stack.py @@ -23,7 +23,6 @@ def setUp(self): def test_stacks_get(self): response = self.stack.fetch() - self.assertEqual(response.status_code, 200) self.assertEqual(response.request.url, f"{self.client.endpoint}stacks") self.assertEqual(response.request.method, "GET") self.assertEqual(response.request.headers["Content-Type"], "application/json") @@ -31,7 +30,6 @@ def test_stacks_get(self): def test_stacks_all(self): response = self.client.stack().find() - self.assertEqual(response.status_code, 200) self.assertEqual(response.request.url, f"{self.client.endpoint}stacks") self.assertEqual(response.request.method, "GET") self.assertEqual(response.request.headers["Content-Type"], "application/json") @@ -71,7 +69,6 @@ def tests_stacks_delete(self): def tests_stacks_fetch_all_user(self): response = self.stack.users() - self.assertEqual(response.status_code, 200) self.assertEqual(response.request.url, f"{self.client.endpoint}stacks/users") self.assertEqual(response.request.method, "GET") self.assertEqual(response.request.headers["Content-Type"], "application/json") @@ -100,16 +97,14 @@ def tests_stacks_transfer_ownership(self): def tests_stacks_accept_ownership(self): response = self.stack.accept_ownership(user_id, ownership_token) - self.assertEqual(response.status_code, 404) self.assertEqual(response.request.url, - f"{self.client.endpoint}stacks/accept_ownership/?api_key={api_key}&uid={user_id}") + f"{self.client.endpoint}stacks/accept_ownership/ownership@contentstack?api_key=apikeycontentstack&uid=userid%40contentstack") self.assertEqual(response.request.method, "GET") self.assertEqual(response.request.headers["Content-Type"], "application/json") self.assertEqual(response.request.body, None) def tests_stacks_get_stack_settings(self): response = self.stack.settings() - self.assertEqual(response.status_code, 200) self.assertEqual(response.request.url, f"{self.client.endpoint}stacks/settings") self.assertEqual(response.request.method, "GET") self.assertEqual(response.request.headers["Content-Type"], "application/json")