From 14190a5e122f439a578dc0118d67837cb90e59e3 Mon Sep 17 00:00:00 2001 From: rmondal00 Date: Thu, 20 Jul 2023 13:06:54 +0200 Subject: [PATCH] LITE-28139 Add capability to validate PPR file using CBC PLM API --- connect_ext_ppr/client/client.py | 1 - connect_ext_ppr/client/exception.py | 9 +- connect_ext_ppr/client/ns.py | 28 +- connect_ext_ppr/services/cbc_hub.py | 12 + tests/conftest.py | 10 + tests/fixtures/Sweet_Pies_v2.xlsx | Bin 0 -> 20056 bytes .../fixtures/parse_ppr_success_response.json | 795 ++++++++++++++++++ tests/services/test_cbc_hub.py | 29 + 8 files changed, 877 insertions(+), 7 deletions(-) create mode 100644 tests/fixtures/Sweet_Pies_v2.xlsx create mode 100644 tests/fixtures/parse_ppr_success_response.json diff --git a/connect_ext_ppr/client/client.py b/connect_ext_ppr/client/client.py index 7a218be..b0825dc 100644 --- a/connect_ext_ppr/client/client.py +++ b/connect_ext_ppr/client/client.py @@ -93,7 +93,6 @@ def execute_request( if response is not None: raise ClientError( message=f'{type(e).__name__} : {str(e)}', - status_code=response.status_code, response=response, cause=e, ) diff --git a/connect_ext_ppr/client/exception.py b/connect_ext_ppr/client/exception.py index 828cc4c..1ad5f44 100644 --- a/connect_ext_ppr/client/exception.py +++ b/connect_ext_ppr/client/exception.py @@ -1,3 +1,5 @@ +from json import JSONDecodeError + from requests import Response @@ -5,11 +7,14 @@ class ClientError(RuntimeError): def __init__( self, message: str, - status_code: int = None, response: Response = None, cause: Exception = None, ): self.message = message self.response = response - self.status_code = status_code + self.status_code = response.status_code if response else None self.cause = cause + try: + self.json = response.json() if response else None + except JSONDecodeError: + self.json = None diff --git a/connect_ext_ppr/client/ns.py b/connect_ext_ppr/client/ns.py index b28832a..bdbbe7b 100644 --- a/connect_ext_ppr/client/ns.py +++ b/connect_ext_ppr/client/ns.py @@ -1,4 +1,6 @@ from functools import cached_property +from io import FileIO +from typing import Dict from connect_ext_ppr.client.mixin import ( ActionMixin, @@ -63,10 +65,7 @@ def collection(self, name: str): ) -class Service( - NSBase, - ActionMixin, -): +class Service(NSBase): def __init__(self, client, aps_type: str, path: str): super().__init__( client=client, @@ -113,3 +112,24 @@ def get(self, **kwargs): path=f'{self.path}/aps/2/resources/?implementing({self.aps_type})', params=kwargs, ) + + def action( + self, + name: str, + method: str = 'POST', + payload: dict = None, + file: FileIO = None, + headers: Dict[str, str] = None, + output: str = 'body', + ): + if payload and file: + raise ValueError('Either payload or file can be specified.') + + return self.client.execute_request( + method=method, + path=f'{self.service_path}/{name}', + payload=payload, + file=file, + headers=headers, + output=output, + ) diff --git a/connect_ext_ppr/services/cbc_hub.py b/connect_ext_ppr/services/cbc_hub.py index 0e48206..70d1ff3 100644 --- a/connect_ext_ppr/services/cbc_hub.py +++ b/connect_ext_ppr/services/cbc_hub.py @@ -1,4 +1,6 @@ +import base64 from functools import cached_property +from io import FileIO from connect_ext_ppr.client import CBCClient from connect_ext_ppr.client.exception import ClientError @@ -81,3 +83,13 @@ def update_product(self, product_id: str): 'fulfillmentSystem': 'connect', }, ) + + def parse_ppr(self, file: FileIO): + base64_content = base64.b64encode(file.read()).decode('ascii') + + return self.plm_service.action( + name='parseConfig', + payload={ + 'excelConfig': base64_content, + }, + ) diff --git a/tests/conftest.py b/tests/conftest.py index 8eb75d0..de3cf36 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -686,3 +686,13 @@ def plm_service(): @pytest.fixture def plm_services(plm_service): return [plm_service] + + +@pytest.fixture +def sample_ppr_file(): + return open('./tests/fixtures/Sweet_Pies_v2.xlsx', 'rb') + + +@pytest.fixture +def parse_ppr_success_response(): + return json.load(open('./tests/fixtures/parse_ppr_success_response.json')) diff --git a/tests/fixtures/Sweet_Pies_v2.xlsx b/tests/fixtures/Sweet_Pies_v2.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ec7c6ae648032f742b328d7016e0a6c3c19f5fbb GIT binary patch literal 20056 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 z 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