pumpwood_communication.microservice_abc.base

Module with base classes for requests on Pumpwood Server.

1"""Module with base classes for requests on Pumpwood Server."""
2from .base import PumpWoodMicroServiceBase
3
4__docformat__ = "google"
5__all__ = [
6    PumpWoodMicroServiceBase]
class PumpWoodMicroServiceBase:
 25class PumpWoodMicroServiceBase:
 26    """Base class for Pumpwood MicroService.
 27
 28    Enviroment variables can be used to set MicroService parameters:
 29    - **PUMPWOOD_COMUNICATION__DEFAULT_TIMEOUT:** Default requests timeout in
 30        seconds.
 31    - **PUMPWOOD_COMUNICATION__DEBUG:** If object will be initiated using
 32        debug parameter. It will have more verbosity and login at each
 33        request. Options 'TRUE', 'FALSE'.
 34    - **PUMPWOOD_COMUNICATION__VERIFY_SSL:** If requests will validate SSL
 35        certificate.
 36    """
 37
 38    __base_header = {'Content-Type': 'application/json'}
 39    """Base header for the requests."""
 40
 41    @staticmethod
 42    def _adjust_server_url(server_url):
 43        """Remove tralling / if present on server URL."""
 44        if server_url is None:
 45            return None
 46        if server_url[-1] != '/':
 47            return server_url + '/'
 48        else:
 49            return server_url
 50
 51    def __init__(self, name: str = None, server_url: str = None,
 52                 username: str = None, password: str = None,
 53                 verify_ssl: bool = True, debug: bool = None,
 54                 default_timeout: int = None, **kwargs):
 55        """Create new PumpWoodMicroService object.
 56
 57        Creates a new microservice object. If just name is passed, object must
 58        be initiate after with init() method.
 59
 60        Args:
 61            name:
 62                Name of the microservice, helps when exceptions
 63                are raised.
 64            server_url:
 65                URL of the server that will be connected.
 66            username:
 67                Username that will be logged on.
 68            password:
 69                Variable to be converted to JSON and posted along
 70                with the request.
 71            verify_ssl:
 72                Set if microservice will verify SSL certificate.
 73            debug:
 74                If microservice will be used as debug mode. This will obrigate
 75                auth token refresh for each call.
 76            default_timeout:
 77                Default timeout for Pumpwood calls.
 78            **kwargs:
 79                Other parameters used for compatibility between versions.
 80
 81        Returns:
 82            PumpWoodMicroService: New PumpWoodMicroService object
 83
 84        Raises:
 85            No particular Raises.
 86        """
 87        # Create attributes to be set at init function
 88        self.name = None
 89        """Name of the microservice instance."""
 90        self.server_url = None
 91        """Pumpwood server URL."""
 92        self._default_timeout: int = None
 93        """Default timeout for Pumpwood requests."""
 94        self._debug: bool = None
 95        """Name of the microservice instance."""
 96        self._verify_ssl: bool = None
 97        """If microservice should check the certificate."""
 98        self._is_mfa_login: bool = None
 99        """Set if is MFA login."""
100        self.__headers: dict = None
101        """Headers to be used on the requests."""
102        self.__user: dict = None
103        """Information of the logged user."""
104        self.__auth_header: dict = None
105        """Authenticated auth header."""
106        self.__token_expiry: pd.Timedelta = None
107        """Expirity datetime of the authetication token."""
108        self.__username: str = None
109        """Username associated with microservice."""
110        self.__password: str = None
111        """Password associated with microservice."""
112        self.init(
113            name=name, server_url=server_url,
114            username=username, password=password,
115            verify_ssl=verify_ssl, debug=debug,
116            default_timeout=default_timeout)
117
118    def init(self, name: str = None, server_url: str = None,
119             username: str = None, password: str = None,
120             verify_ssl: bool = True, debug: bool = None,
121             default_timeout: int = None, **kwargs):
122        """Lazzy initialization of the MicroService of object.
123
124        This function might be usefull to use the object as a singleton at
125        the backends. Using this function it is possible to instanciate an
126        empty object and them set the attributes latter at the systems.
127
128        Args:
129            name:
130                Name of the microservice, helps when exceptions
131                are raised.
132            server_url:
133                URL of the server that will be connected.
134            username:
135                Username that will be logged on.
136            password:
137                Variable to be converted to JSON and posted along
138                with the request.
139            verify_ssl:
140                Set if microservice will verify SSL certificate.
141            debug:
142                If microservice will be used as debug mode. This will obrigate
143                auth token refresh for each call.
144            default_timeout:
145                Default timeout for Pumpwood calls.
146            **kwargs:
147                Other parameters used for compatibility between versions.
148
149        Returns:
150            No return
151
152        Raises:
153            No particular Raises
154        """
155        self.name = name
156        """Name of the microservice instance."""
157        self.server_url = self._adjust_server_url(server_url)
158        """Pumpwood server URL."""
159
160        # Set parameter using arguments or enviroment variables
161        if default_timeout is None:
162            self._default_timeout = int(os.getenv(
163                'PUMPWOOD_COMUNICATION__DEFAULT_TIMEOUT', 60))
164        else:
165            self._default_timeout = default_timeout
166
167        if debug is None:
168            self._debug = os.getenv(
169                'PUMPWOOD_COMUNICATION__DEBUG', 'FALSE') == 'TRUE'
170        else:
171            self._debug = debug
172
173        if verify_ssl is None:
174            self._verify_ssl = os.getenv(
175                'PUMPWOOD_COMUNICATION__VERIFY_SSL', 'TRUE') == 'TRUE'
176        else:
177            self._verify_ssl = verify_ssl
178
179        self._is_mfa_login = False
180        self.__headers = None
181        self.__user = None
182        self.__auth_header = None
183        self.__token_expiry = None
184        self.__username = username
185        self.__password = password
186
187    @staticmethod
188    def angular_json(request_result) -> Any:
189        r"""Convert text to Json removing any XSSI at the beging of JSON.
190
191        Some backends add `)]}',\n` at the beginning of the JSON data to
192        prevent injection of functions. This function remove this characters
193        if present.
194
195        Args:
196            request_result:
197                JSON request to be converted.
198
199        Returns:
200            No return
201
202        Raises:
203            PumpWoodJSONLoadError:
204                If it is not possible to load JSON from request data.
205        """
206        if request_result.text == '':
207            return None
208
209        string_start = ")]}',\n"
210        try:
211            if request_result.text[:6] == string_start:
212                return (orjson.loads(request_result.text[6:]))
213            else:
214                return (orjson.loads(request_result.text))
215        except Exception:
216            msg = "Can not decode to Json"
217            raise PumpWoodJSONLoadError(
218                message=msg, payload={"request_data": request_result.text})
219
220    def time_to_expiry(self) -> pd.Timedelta:
221        """Return time to token expiry.
222
223        Args:
224            No Args.
225
226        Returns:
227            Return time until token expiration.
228        """
229        if self.__token_expiry is None:
230            return None
231
232        now_datetime = pd.to_datetime(
233            datetime.datetime.now(datetime.UTC), utc=True)
234        time_to_expiry = self.__token_expiry - now_datetime
235        return time_to_expiry
236
237    def is_credential_set(self) -> bool:
238        """Check if username and password are set on object.
239
240        Args:
241            No Args.
242
243        Returns:
244            True if usename and password were set during object creation or
245            later with init function.
246        """
247        is_username_not_none = self.__username is not None
248        is_password_not_none = self.__password is not None
249        return is_username_not_none and is_password_not_none
250
251    @classmethod
252    def is_invalid_token_response(cls, response: Response) -> bool:
253        """Check if reponse has invalid token error.
254
255        Args:
256            response:
257                Request reponse to check for invalid token.
258
259        Returns:
260            Return True if response has an invalid token status.
261        """
262        if response.status_code == 401:
263            return True
264        return False
265
266    def _login_resquest(self) -> Response:
267        """Request login with object credentials.
268
269        Args:
270            No Args
271
272        Returns (Response):
273            Request object.
274        """
275        login_url = urljoin(
276            self.server_url, 'rest/registration/login/')
277
278        # Make a retry loop for authentication
279        login_result = None
280        for i in range(5):
281            login_result = requests.post(
282                login_url, json={
283                    'username': self.__username,
284                    'password': self.__password},
285                verify=self._verify_ssl, timeout=self._default_timeout)
286            if login_result.ok:
287                try:
288                    login_data = self.angular_json(login_result)
289                    return login_data
290                except Exception: # NOQA
291                    pass
292
293            # Handle Unauthorized responses
294            elif self.is_invalid_token_response(login_result):
295                error_data = self.angular_json(login_result)
296                raise PumpWoodUnauthorized(
297                    message="Login is not possible",
298                    payload=error_data)
299
300            # Handle Forbidden responses
301            elif login_result.status_code == 403:
302                error_data = self.angular_json(login_result)
303                raise PumpWoodForbidden(
304                    message="Login resquest is forbidden",
305                    payload=error_data)
306            time.sleep(0.2)
307
308        # If response is not returned then something is not ok
309        error_data = self.angular_json(login_result)
310        raise PumpWoodOtherException(
311            message="Login not possible, server is not responding correctly",
312            payload=error_data)
313
314    def confirm_mfa_code(self, mfa_login_data: dict) -> dict:
315        """Ask user to confirm MFA code to login.
316
317        Open an input interface at terminal for user to validate MFA token.
318
319        Args:
320            mfa_login_data:
321                Result from login request with 'mfa_token'
322                as key.
323
324        Returns:
325            Return login returned with MFA confimation.
326
327        Raise:
328            Raise error if reponse is not valid using error_handler.
329        """
330        code = input("## Please enter MFA code: ")
331        url = urljoin(
332            self.server_url, 'rest/registration/mfa-validate-code/')
333        mfa_response = requests.post(url, headers={
334            "X-PUMPWOOD-MFA-Autorization": mfa_login_data['mfa_token']},
335            json={"mfa_code": code}, timeout=self._default_timeout)
336        self.error_handler(mfa_response)
337
338        # Set _is_mfa_login true to indicate that login required MFA
339        self._is_mfa_login = True
340        return self.angular_json(mfa_response)
341
342    def login(self, force_refresh: bool = False) -> None:
343        """Log microservice in using username and password provided.
344
345        Args:
346            force_refresh (bool):
347                Force token refresh despise still valid
348                according to self.__token_expiry.
349
350        Returns:
351            No return
352
353        Raises:
354            Exception:
355                If login response has status diferent from 200.
356        """
357        if not self.is_credential_set():
358            raise PumpWoodUnauthorized(
359                message="Microservice username or/and password not set")
360
361        # Check if expiry time is 1h from now
362        refresh_expiry = False
363        if self.__token_expiry is None:
364            refresh_expiry = True
365        else:
366            time_to_expiry = self.time_to_expiry()
367            if time_to_expiry < datetime.timedelta(hours=1):
368                refresh_expiry = True
369
370        # When if debug always refresh token
371        if refresh_expiry or force_refresh or self._debug:
372            login_data = self._login_resquest()
373            if 'mfa_token' in login_data.keys():
374                login_data = self.confirm_mfa_code(
375                    mfa_login_data=login_data)
376
377            self.__auth_header = {
378                'Authorization': 'Token ' + login_data['token']}
379            self.__user = login_data["user"]
380            self.__token_expiry = pd.to_datetime(login_data['expiry'])
381        else:
382            # Token is not expired or envicted, them keep same token
383            return None
384
385    def logout(self, auth_header: dict = None) -> bool:
386        """Logout token.
387
388        Args:
389            auth_header:
390                Authentication header.
391
392        Returns:
393            True if logout was ok.
394        """
395        resp = self.request_post(
396            url='rest/registration/logout/',
397            data={}, auth_header=auth_header)
398        # Set expiry to None to envict the token
399        self.__token_expiry = None
400        return resp is None
401
402    def logout_all(self, auth_header: dict = None) -> bool:
403        """Logout all tokens from user.
404
405        Args:
406            auth_header (dict):
407                Authentication header.
408
409        Returns:
410            True if logout all was ok.
411        """
412        resp = self.request_post(
413            url='rest/registration/logoutall/',
414            data={}, auth_header=auth_header)
415        # Set expiry to None to envict the token
416        self.__token_expiry = None
417        return resp is None
418
419    def get_auth_header(self) -> dict:
420        """Retrieve auth_header and token_expiry from object.
421
422        Args:
423            No Args.
424
425        Returns:
426            Return authorization header and token_expiry datetime from object.
427        """
428        # Copy the dictonary to avoid updating the original one
429        return copy.deepcopy({
430            "auth_header": self.__auth_header,
431            "token_expiry": self.__token_expiry})
432
433    def _check_auth_header(self, auth_header: dict,
434                           multipart: bool = False) -> dict:
435        """Check if auth_header is set or auth_header if provided.
436
437        Args:
438            auth_header (dict):
439                AuthHeader to substitute the microservice original
440                at the request (user impersonation).
441            multipart (dict):
442                Set if call should be made as a multipart instead of JSON.
443
444        Returns (dict):
445            Return a header dict to be used in requests.
446
447        Raises:
448            PumpWoodUnauthorized:
449                If microservice is not logged and a auth_header method
450                argument is not provided.
451            PumpWoodUnauthorized:
452                If microservice is logged and a auth_header method argument
453                is provided.
454        """
455        if auth_header is None:
456            # Login will refresh token if it is 1h to expire, it will also
457            # check if credentials are set.
458            self.login()
459            auth_header_data = self.get_auth_header()
460            auth_header = auth_header_data['auth_header']
461            if multipart:
462                return auth_header
463            else:
464                return self.__base_header | auth_header
465        else:
466            if self.is_credential_set():
467                msg = (
468                    'Microservice [{object_name}] with credentials and '
469                    'auth_header was provided')
470                raise PumpWoodUnauthorized(
471                    message=msg, payload={'object_name': self.name})
472
473            # Set base header as JSON since unserialization is done using
474            # Pumpwood Communication serialization function
475            temp__auth_header = auth_header.copy()
476            if multipart:
477                return temp__auth_header
478            else:
479                return self.__base_header | temp__auth_header
480
481    @classmethod
482    def error_handler(cls, response):
483        """Handle request error.
484
485        Check if is a Json and propagate the error with
486        same type if possible. If not Json raises the content.
487
488        Args:
489            response:
490                response to be handled, it is a PumpWoodException
491                return it will raise the same exception at microservice
492                object.
493
494        Returns:
495            No return.
496
497        Raises:
498            PumpWoodOtherException:
499                If content-type is not application/json.
500            PumpWoodOtherException:
501                If content-type is application/json, but type not
502                present or not recognisable at `exceptions.exceptions_dict`.
503            Other PumpWoodException sub-types:
504                If content-type is application/json if type is present and
505                recognisable.
506
507        Example:
508            No example
509        """
510        if not response.ok:
511            utcnow = datetime.datetime.now(datetime.UTC)
512            response_content_type = response.headers['content-type']
513
514            # Request information
515            url = response.url
516            method = response.request.method
517            if 'application/json' not in response_content_type.lower():
518                # Raise the exception as first in exception deep.
519                exception_dict = [{
520                    "exception_url": url,
521                    "exception_method": method,
522                    "exception_utcnow": utcnow.isoformat(),
523                    "exception_deep": 1}]
524                raise PumpWoodOtherException(
525                    message=response.text, payload={
526                        "!exception_stack!": exception_dict})
527
528            # Build error stack
529            response_dict = cls.angular_json(response)
530
531            # Removing previous error stack
532            payload = copy.deepcopy(
533                response_dict.get("payload", {}))
534            exception_stack = copy.deepcopy(
535                payload.pop("!exception_stack!", []))
536
537            exception_deep = len(exception_stack)
538            exception_dict = {
539                "exception_url": url,
540                "exception_method": method,
541                "exception_utcnow": utcnow.isoformat(),
542                "exception_deep": exception_deep + 1
543            }
544            exception_stack.insert(0, exception_dict)
545            payload["!exception_stack!"] = exception_stack
546
547            ###################
548            # Propagate error #
549            # get exception using 'type' key at response data and get the
550            # exception from exceptions_dict at exceptions
551            exception_message = response_dict.get("message", "")
552            exception_type = response_dict.get("type", None)
553            TempPumpwoodException = exceptions_dict.get(exception_type)
554            if TempPumpwoodException is not None:
555                raise TempPumpwoodException(
556                    message=exception_message,
557                    status_code=response.status_code,
558                    payload=payload)
559            else:
560                # If token is invalid is at response, return a
561                # PumpWoodUnauthorized error
562                is_invalid_token = cls.is_invalid_token_response(response)
563                response_dict["!exception_stack!"] = exception_stack
564                if is_invalid_token:
565                    raise PumpWoodUnauthorized(
566                        message="Invalid token", payload=payload)
567                else:
568                    # If the error is not mapped return a
569                    # PumpWoodOtherException limiting the message size to 1k
570                    # characters
571                    raise PumpWoodOtherException(
572                        message="Not mapped exception JSON",
573                        payload=response_dict)
574
575    def _request_post_json(self, post_url: str, data: any,
576                           auth_header: dict = None,
577                           parameters: dict = {}) -> any:
578        """Make a POST a request to url with data as JSON payload.
579
580        Args:
581            post_url:
582                URL to make the request, already with server url.
583            data:
584                Data to be used as Json payload.
585            parameters:
586                URL parameters.
587            auth_header:
588                AuthHeader to substitute the microservice original
589                at the request (user impersonation).
590
591        Returns:
592            Return the post response data.
593
594        Raises:
595            PumpWoodException sub-types:
596                Response is passed to error_handler.
597        """
598        response = None
599        request_header = self._check_auth_header(auth_header=auth_header)
600        dumped_data = pumpJsonDump(data)
601        response = requests.post(
602            url=post_url, data=dumped_data,
603            params=parameters, verify=self._verify_ssl,
604            headers=request_header, timeout=self._default_timeout)
605
606        # Retry request if token is not valid forcing token renew
607        retry_with_login = (
608            self.is_invalid_token_response(response) and
609            auth_header is None)
610        if not retry_with_login:
611            return response
612        else:
613            # Force token refresh if Unauthorized
614            time.sleep(0.5)
615            self.login(force_refresh=True)
616            request_header = self._check_auth_header(auth_header=auth_header)
617            return requests.post(
618                url=post_url, data=dumped_data,
619                params=parameters, verify=self._verify_ssl,
620                headers=request_header, timeout=self._default_timeout)
621
622    def _request_post_multi(self, post_url: str, data: any, files: list = None,
623                            auth_header: dict = None,
624                            parameters: dict = {}) -> any:
625        """Make a POST a request to url with data as multipart payload.
626
627        Args:
628            post_url:
629                URL to make the request, already with server url.
630            data:
631                Data to be used as Json payload.
632            files:
633                A dictonary with file data, files will be set on field
634                corresponding.to dictonary key.
635                `{'file1': open('file1', 'rb'), {'file2': open('file2', 'rb')}`
636            parameters:
637                URL parameters.
638            auth_header:
639                AuthHeader to substitute the microservice original
640                at the request (user impersonation).
641
642        Returns:
643            Return the post response data.
644
645        Raises:
646            PumpWoodException sub-types:
647                Response is passed to error_handler.
648        """
649        # Request with files are done using multipart serializing all fields
650        # as JSON
651        request_header = self._check_auth_header(
652            auth_header=auth_header, multipart=True)
653        temp_data = {'__json__': pumpJsonDump(data)}
654
655        response = requests.post(
656            url=post_url, data=temp_data, files=files, params=parameters,
657            verify=self._verify_ssl, headers=request_header,
658            timeout=self._default_timeout)
659        retry_with_login = (
660            self.is_invalid_token_response(response) and
661            auth_header is None)
662        if not retry_with_login:
663            return response
664        else:
665            # Force token refresh if Unauthorized
666            time.sleep(0.5)
667            self.login(force_refresh=True)
668            request_header = self._check_auth_header(
669                auth_header=auth_header, multipart=True)
670            return requests.post(
671                url=post_url, data=temp_data, files=files,
672                params=parameters, verify=self._verify_ssl,
673                headers=request_header, timeout=self._default_timeout)
674
675    @classmethod
676    def _treat_response_for_file(cls, response: Response) -> dict:
677        """Return if response contain a file.
678
679        Args:
680            response (Response):
681                Response to be checked for a file content.
682
683        Returns (bool):
684            Returns if reponse has a file.
685        """
686        headers = response.headers
687        content_disposition = headers.get('content-disposition')
688        if content_disposition is None:
689            return cls.angular_json(response)
690        else:
691            fname = re.findall("filename=(.+)", content_disposition)[0]
692            return {
693                "__file__": True,
694                "content": response.content,
695                "content-type": response.headers['content-type'],
696                "filename": fname}
697
698    @classmethod
699    def _dump_query_parameters(cls, parameters: dict) -> dict:
700        """Dump query parameters to javascript compatibility.
701
702        Args:
703            parameters (dict):
704                Parameters to be parsed to JSON.
705
706        Returns:
707            pass
708        """
709        # If parameters are not none convert them to json before
710        # sending information on query string, 'True' is 'true' on javascript
711        # for example
712        if parameters is not None:
713            temp_parameters = copy.deepcopy(parameters)
714            for key in temp_parameters.keys():
715                # Do not convert str to json, it put extra "" araound string
716                if type(temp_parameters[key]) is not str:
717                    temp_parameters[key] = pumpJsonDump(parameters[key])
718            return temp_parameters
719        else:
720            return None
721
722    def request_post(self, url: str, data: any, files: list = None,
723                     auth_header: dict = None,
724                     parameters: dict = {}) -> any:
725        """Make a POST a request to url with data as multipart/json payload.
726
727        Args:
728            url:
729                URL to make the request, already with server url.
730            data:
731                Data to be used as Json payload.
732            files:
733                A dictonary with file data, files will be set on field
734                corresponding.to dictonary key.
735                `{'file1': open('file1', 'rb'), {'file2': open('file2', 'rb')}`
736            parameters:
737                URL parameters.
738            auth_header:
739                AuthHeader to substitute the microservice original
740                at the request (user impersonation).
741
742        Returns:
743            Return the post response data.
744
745        Raises:
746            PumpWoodException sub-types:
747                Response is passed to error_handler.
748        """
749        post_url = urljoin(self.server_url, url)
750        dumped_parameters = self._dump_query_parameters(parameters=parameters)
751        response = None
752        if files is None:
753            response = self._request_post_json(
754                post_url=post_url, data=data, auth_header=auth_header,
755                parameters=dumped_parameters)
756        else:
757            response = self._request_post_multi(
758                post_url=post_url, data=data, files=files,
759                auth_header=auth_header, parameters=dumped_parameters)
760
761        # Handle errors and re-raise if Pumpwood Exceptions
762        self.error_handler(response)
763        return self._treat_response_for_file(response=response)
764
765    def request_get(self, url: str, parameters: dict = {},
766                    auth_header: dict = None,
767                    use_disk_cache: bool = False,
768                    disk_cache_expire: int = None) -> Any:
769        """Make a GET a request to url with data as JSON payload.
770
771        Add the auth_header acording to login information and refresh token
772        if auth_header=None and object token is expired.
773
774        Args:
775            url (str):
776                URL to make the request.
777            parameters (dict):
778                URL parameters to make the request.
779            auth_header (dict):
780                Auth header to substitute the microservice original
781                at the request (user impersonation).
782            use_disk_cache (bool):
783                If set true, get request will use local cache to reduce
784                the requests to the backend.
785            disk_cache_expire (int):
786                Time in seconds to expire the cache, it None it will
787                use de default set be PumpwoodCache.
788
789        Returns:
790            Return the post reponse data.
791
792        Raises:
793            PumpWoodException sub-types:
794                Raise exception if reponse is not 2XX and if 'type' key on
795                JSON payload if found at exceptions_dict. Use the same
796                exception, message and payload.
797            PumpWoodOtherException:
798                If exception type is not found or return is not a json.
799        """
800        request_header = self._check_auth_header(auth_header)
801        # If is set to use diskcache, it will create a hash cash using
802        # the query paramerers, url and user access token. The
803        # hash will be used as index, not exposing the token at cache
804        # database
805        hash_dict = {}
806        if use_disk_cache:
807            hash_dict['authorization'] = request_header['Authorization']
808            hash_dict['parameters'] = parameters
809            hash_dict['url'] = url
810            cache_results = default_cache.get(hash_dict=hash_dict)
811            if cache_results is not None:
812                msg = "get from cache url[{url}]".format(url=url)
813                logger.info(msg)
814                return cache_results
815
816        dumped_parameters = self._dump_query_parameters(parameters=parameters)
817        get_url = urljoin(self.server_url, url)
818        response = requests.get(
819            get_url, verify=self._verify_ssl, headers=request_header,
820            params=dumped_parameters, timeout=self._default_timeout)
821
822        # If token is expired, refresh it
823        retry_with_login = (
824            self.is_invalid_token_response(response) and
825            auth_header is None)
826        if retry_with_login:
827            time.sleep(0.5)
828            self.login(force_refresh=True)
829            request_header = self._check_auth_header(auth_header=auth_header)
830            response = requests.get(
831                get_url, verify=self._verify_ssl, headers=request_header,
832                params=dumped_parameters, timeout=self._default_timeout)
833
834        # Re-raise Pumpwood exceptions
835        self.error_handler(response=response)
836        results = self._treat_response_for_file(response=response)
837
838        # If is set to use cache for this calls, set the local cache
839        if use_disk_cache and not results.get('__file__', False):
840            default_cache.set(
841                hash_dict=hash_dict, value=results,
842                expire=disk_cache_expire)
843        return results
844
845    def request_delete(self, url, parameters: dict = None,
846                       auth_header: dict = None):
847        """Make a DELETE a request to url with data as Json payload.
848
849        Args:
850            url:
851                Url to make the request.
852            parameters:
853                Dictionary with Urls parameters.
854            auth_header:
855                Auth header to substitute the microservice original
856                at the request (user impersonation).
857
858        Returns:
859            Return the delete reponse payload.
860
861        Raises:
862            PumpWoodException sub-types:
863                Raise exception if reponse is not 2XX and if 'type' key on
864                JSON payload if found at exceptions_dict. Use the same
865                exception, message and payload.
866            PumpWoodOtherException:
867                If exception type is not found or return is not a json.
868        """
869        request_header = self._check_auth_header(auth_header)
870        dumped_parameters = self._dump_query_parameters(parameters=parameters)
871
872        post_url = self.server_url + url
873        response = requests.delete(
874            post_url, verify=self._verify_ssl, headers=request_header,
875            params=dumped_parameters, timeout=self._default_timeout)
876
877        # Retry request if token is not valid forcing token renew
878        retry_with_login = (
879            self.is_invalid_token_response(response) and
880            auth_header is None)
881        if retry_with_login:
882            time.sleep(0.5)
883            self.login(force_refresh=True)
884            request_header = self._check_auth_header(auth_header=auth_header)
885            response = requests.delete(
886                post_url, verify=self._verify_ssl, headers=request_header,
887                params=dumped_parameters, timeout=self._default_timeout)
888
889        # Re-raise Pumpwood Exceptions
890        self.error_handler(response)
891        return self.angular_json(response)

Base class for Pumpwood MicroService.

Enviroment variables can be used to set MicroService parameters:

  • PUMPWOOD_COMUNICATION__DEFAULT_TIMEOUT: Default requests timeout in seconds.
  • PUMPWOOD_COMUNICATION__DEBUG: If object will be initiated using debug parameter. It will have more verbosity and login at each request. Options 'TRUE', 'FALSE'.
  • PUMPWOOD_COMUNICATION__VERIFY_SSL: If requests will validate SSL certificate.
PumpWoodMicroServiceBase( name: str = None, server_url: str = None, username: str = None, password: str = None, verify_ssl: bool = True, debug: bool = None, default_timeout: int = None, **kwargs)
 51    def __init__(self, name: str = None, server_url: str = None,
 52                 username: str = None, password: str = None,
 53                 verify_ssl: bool = True, debug: bool = None,
 54                 default_timeout: int = None, **kwargs):
 55        """Create new PumpWoodMicroService object.
 56
 57        Creates a new microservice object. If just name is passed, object must
 58        be initiate after with init() method.
 59
 60        Args:
 61            name:
 62                Name of the microservice, helps when exceptions
 63                are raised.
 64            server_url:
 65                URL of the server that will be connected.
 66            username:
 67                Username that will be logged on.
 68            password:
 69                Variable to be converted to JSON and posted along
 70                with the request.
 71            verify_ssl:
 72                Set if microservice will verify SSL certificate.
 73            debug:
 74                If microservice will be used as debug mode. This will obrigate
 75                auth token refresh for each call.
 76            default_timeout:
 77                Default timeout for Pumpwood calls.
 78            **kwargs:
 79                Other parameters used for compatibility between versions.
 80
 81        Returns:
 82            PumpWoodMicroService: New PumpWoodMicroService object
 83
 84        Raises:
 85            No particular Raises.
 86        """
 87        # Create attributes to be set at init function
 88        self.name = None
 89        """Name of the microservice instance."""
 90        self.server_url = None
 91        """Pumpwood server URL."""
 92        self._default_timeout: int = None
 93        """Default timeout for Pumpwood requests."""
 94        self._debug: bool = None
 95        """Name of the microservice instance."""
 96        self._verify_ssl: bool = None
 97        """If microservice should check the certificate."""
 98        self._is_mfa_login: bool = None
 99        """Set if is MFA login."""
100        self.__headers: dict = None
101        """Headers to be used on the requests."""
102        self.__user: dict = None
103        """Information of the logged user."""
104        self.__auth_header: dict = None
105        """Authenticated auth header."""
106        self.__token_expiry: pd.Timedelta = None
107        """Expirity datetime of the authetication token."""
108        self.__username: str = None
109        """Username associated with microservice."""
110        self.__password: str = None
111        """Password associated with microservice."""
112        self.init(
113            name=name, server_url=server_url,
114            username=username, password=password,
115            verify_ssl=verify_ssl, debug=debug,
116            default_timeout=default_timeout)

Create new PumpWoodMicroService object.

Creates a new microservice object. If just name is passed, object must be initiate after with init() method.

Arguments:
  • name: Name of the microservice, helps when exceptions are raised.
  • server_url: URL of the server that will be connected.
  • username: Username that will be logged on.
  • password: Variable to be converted to JSON and posted along with the request.
  • verify_ssl: Set if microservice will verify SSL certificate.
  • debug: If microservice will be used as debug mode. This will obrigate auth token refresh for each call.
  • default_timeout: Default timeout for Pumpwood calls.
  • **kwargs: Other parameters used for compatibility between versions.
Returns:

PumpWoodMicroService: New PumpWoodMicroService object

Raises:
  • No particular Raises.
name

Name of the microservice instance.

server_url

Pumpwood server URL.

def init( self, name: str = None, server_url: str = None, username: str = None, password: str = None, verify_ssl: bool = True, debug: bool = None, default_timeout: int = None, **kwargs):
118    def init(self, name: str = None, server_url: str = None,
119             username: str = None, password: str = None,
120             verify_ssl: bool = True, debug: bool = None,
121             default_timeout: int = None, **kwargs):
122        """Lazzy initialization of the MicroService of object.
123
124        This function might be usefull to use the object as a singleton at
125        the backends. Using this function it is possible to instanciate an
126        empty object and them set the attributes latter at the systems.
127
128        Args:
129            name:
130                Name of the microservice, helps when exceptions
131                are raised.
132            server_url:
133                URL of the server that will be connected.
134            username:
135                Username that will be logged on.
136            password:
137                Variable to be converted to JSON and posted along
138                with the request.
139            verify_ssl:
140                Set if microservice will verify SSL certificate.
141            debug:
142                If microservice will be used as debug mode. This will obrigate
143                auth token refresh for each call.
144            default_timeout:
145                Default timeout for Pumpwood calls.
146            **kwargs:
147                Other parameters used for compatibility between versions.
148
149        Returns:
150            No return
151
152        Raises:
153            No particular Raises
154        """
155        self.name = name
156        """Name of the microservice instance."""
157        self.server_url = self._adjust_server_url(server_url)
158        """Pumpwood server URL."""
159
160        # Set parameter using arguments or enviroment variables
161        if default_timeout is None:
162            self._default_timeout = int(os.getenv(
163                'PUMPWOOD_COMUNICATION__DEFAULT_TIMEOUT', 60))
164        else:
165            self._default_timeout = default_timeout
166
167        if debug is None:
168            self._debug = os.getenv(
169                'PUMPWOOD_COMUNICATION__DEBUG', 'FALSE') == 'TRUE'
170        else:
171            self._debug = debug
172
173        if verify_ssl is None:
174            self._verify_ssl = os.getenv(
175                'PUMPWOOD_COMUNICATION__VERIFY_SSL', 'TRUE') == 'TRUE'
176        else:
177            self._verify_ssl = verify_ssl
178
179        self._is_mfa_login = False
180        self.__headers = None
181        self.__user = None
182        self.__auth_header = None
183        self.__token_expiry = None
184        self.__username = username
185        self.__password = password

Lazzy initialization of the MicroService of object.

This function might be usefull to use the object as a singleton at the backends. Using this function it is possible to instanciate an empty object and them set the attributes latter at the systems.

Arguments:
  • name: Name of the microservice, helps when exceptions are raised.
  • server_url: URL of the server that will be connected.
  • username: Username that will be logged on.
  • password: Variable to be converted to JSON and posted along with the request.
  • verify_ssl: Set if microservice will verify SSL certificate.
  • debug: If microservice will be used as debug mode. This will obrigate auth token refresh for each call.
  • default_timeout: Default timeout for Pumpwood calls.
  • **kwargs: Other parameters used for compatibility between versions.
Returns:

No return

Raises:
  • No particular Raises
@staticmethod
def angular_json(request_result) -> Any:
187    @staticmethod
188    def angular_json(request_result) -> Any:
189        r"""Convert text to Json removing any XSSI at the beging of JSON.
190
191        Some backends add `)]}',\n` at the beginning of the JSON data to
192        prevent injection of functions. This function remove this characters
193        if present.
194
195        Args:
196            request_result:
197                JSON request to be converted.
198
199        Returns:
200            No return
201
202        Raises:
203            PumpWoodJSONLoadError:
204                If it is not possible to load JSON from request data.
205        """
206        if request_result.text == '':
207            return None
208
209        string_start = ")]}',\n"
210        try:
211            if request_result.text[:6] == string_start:
212                return (orjson.loads(request_result.text[6:]))
213            else:
214                return (orjson.loads(request_result.text))
215        except Exception:
216            msg = "Can not decode to Json"
217            raise PumpWoodJSONLoadError(
218                message=msg, payload={"request_data": request_result.text})

Convert text to Json removing any XSSI at the beging of JSON.

Some backends add )]}',\n at the beginning of the JSON data to prevent injection of functions. This function remove this characters if present.

Arguments:
  • request_result: JSON request to be converted.
Returns:

No return

Raises:
  • PumpWoodJSONLoadError: If it is not possible to load JSON from request data.
def time_to_expiry(self) -> pandas._libs.tslibs.timedeltas.Timedelta:
220    def time_to_expiry(self) -> pd.Timedelta:
221        """Return time to token expiry.
222
223        Args:
224            No Args.
225
226        Returns:
227            Return time until token expiration.
228        """
229        if self.__token_expiry is None:
230            return None
231
232        now_datetime = pd.to_datetime(
233            datetime.datetime.now(datetime.UTC), utc=True)
234        time_to_expiry = self.__token_expiry - now_datetime
235        return time_to_expiry

Return time to token expiry.

Arguments:
  • No Args.
Returns:

Return time until token expiration.

def is_credential_set(self) -> bool:
237    def is_credential_set(self) -> bool:
238        """Check if username and password are set on object.
239
240        Args:
241            No Args.
242
243        Returns:
244            True if usename and password were set during object creation or
245            later with init function.
246        """
247        is_username_not_none = self.__username is not None
248        is_password_not_none = self.__password is not None
249        return is_username_not_none and is_password_not_none

Check if username and password are set on object.

Arguments:
  • No Args.
Returns:

True if usename and password were set during object creation or later with init function.

@classmethod
def is_invalid_token_response(cls, response: requests.models.Response) -> bool:
251    @classmethod
252    def is_invalid_token_response(cls, response: Response) -> bool:
253        """Check if reponse has invalid token error.
254
255        Args:
256            response:
257                Request reponse to check for invalid token.
258
259        Returns:
260            Return True if response has an invalid token status.
261        """
262        if response.status_code == 401:
263            return True
264        return False

Check if reponse has invalid token error.

Arguments:
  • response: Request reponse to check for invalid token.
Returns:

Return True if response has an invalid token status.

def confirm_mfa_code(self, mfa_login_data: dict) -> dict:
314    def confirm_mfa_code(self, mfa_login_data: dict) -> dict:
315        """Ask user to confirm MFA code to login.
316
317        Open an input interface at terminal for user to validate MFA token.
318
319        Args:
320            mfa_login_data:
321                Result from login request with 'mfa_token'
322                as key.
323
324        Returns:
325            Return login returned with MFA confimation.
326
327        Raise:
328            Raise error if reponse is not valid using error_handler.
329        """
330        code = input("## Please enter MFA code: ")
331        url = urljoin(
332            self.server_url, 'rest/registration/mfa-validate-code/')
333        mfa_response = requests.post(url, headers={
334            "X-PUMPWOOD-MFA-Autorization": mfa_login_data['mfa_token']},
335            json={"mfa_code": code}, timeout=self._default_timeout)
336        self.error_handler(mfa_response)
337
338        # Set _is_mfa_login true to indicate that login required MFA
339        self._is_mfa_login = True
340        return self.angular_json(mfa_response)

Ask user to confirm MFA code to login.

Open an input interface at terminal for user to validate MFA token.

Arguments:
  • mfa_login_data: Result from login request with 'mfa_token' as key.
Returns:

Return login returned with MFA confimation.

Raise:

Raise error if reponse is not valid using error_handler.

def login(self, force_refresh: bool = False) -> None:
342    def login(self, force_refresh: bool = False) -> None:
343        """Log microservice in using username and password provided.
344
345        Args:
346            force_refresh (bool):
347                Force token refresh despise still valid
348                according to self.__token_expiry.
349
350        Returns:
351            No return
352
353        Raises:
354            Exception:
355                If login response has status diferent from 200.
356        """
357        if not self.is_credential_set():
358            raise PumpWoodUnauthorized(
359                message="Microservice username or/and password not set")
360
361        # Check if expiry time is 1h from now
362        refresh_expiry = False
363        if self.__token_expiry is None:
364            refresh_expiry = True
365        else:
366            time_to_expiry = self.time_to_expiry()
367            if time_to_expiry < datetime.timedelta(hours=1):
368                refresh_expiry = True
369
370        # When if debug always refresh token
371        if refresh_expiry or force_refresh or self._debug:
372            login_data = self._login_resquest()
373            if 'mfa_token' in login_data.keys():
374                login_data = self.confirm_mfa_code(
375                    mfa_login_data=login_data)
376
377            self.__auth_header = {
378                'Authorization': 'Token ' + login_data['token']}
379            self.__user = login_data["user"]
380            self.__token_expiry = pd.to_datetime(login_data['expiry'])
381        else:
382            # Token is not expired or envicted, them keep same token
383            return None

Log microservice in using username and password provided.

Arguments:
  • force_refresh (bool): Force token refresh despise still valid according to self.__token_expiry.
Returns:

No return

Raises:
  • Exception: If login response has status diferent from 200.
def logout(self, auth_header: dict = None) -> bool:
385    def logout(self, auth_header: dict = None) -> bool:
386        """Logout token.
387
388        Args:
389            auth_header:
390                Authentication header.
391
392        Returns:
393            True if logout was ok.
394        """
395        resp = self.request_post(
396            url='rest/registration/logout/',
397            data={}, auth_header=auth_header)
398        # Set expiry to None to envict the token
399        self.__token_expiry = None
400        return resp is None

Logout token.

Arguments:
  • auth_header: Authentication header.
Returns:

True if logout was ok.

def logout_all(self, auth_header: dict = None) -> bool:
402    def logout_all(self, auth_header: dict = None) -> bool:
403        """Logout all tokens from user.
404
405        Args:
406            auth_header (dict):
407                Authentication header.
408
409        Returns:
410            True if logout all was ok.
411        """
412        resp = self.request_post(
413            url='rest/registration/logoutall/',
414            data={}, auth_header=auth_header)
415        # Set expiry to None to envict the token
416        self.__token_expiry = None
417        return resp is None

Logout all tokens from user.

Arguments:
  • auth_header (dict): Authentication header.
Returns:

True if logout all was ok.

def get_auth_header(self) -> dict:
419    def get_auth_header(self) -> dict:
420        """Retrieve auth_header and token_expiry from object.
421
422        Args:
423            No Args.
424
425        Returns:
426            Return authorization header and token_expiry datetime from object.
427        """
428        # Copy the dictonary to avoid updating the original one
429        return copy.deepcopy({
430            "auth_header": self.__auth_header,
431            "token_expiry": self.__token_expiry})

Retrieve auth_header and token_expiry from object.

Arguments:
  • No Args.
Returns:

Return authorization header and token_expiry datetime from object.

@classmethod
def error_handler(cls, response):
481    @classmethod
482    def error_handler(cls, response):
483        """Handle request error.
484
485        Check if is a Json and propagate the error with
486        same type if possible. If not Json raises the content.
487
488        Args:
489            response:
490                response to be handled, it is a PumpWoodException
491                return it will raise the same exception at microservice
492                object.
493
494        Returns:
495            No return.
496
497        Raises:
498            PumpWoodOtherException:
499                If content-type is not application/json.
500            PumpWoodOtherException:
501                If content-type is application/json, but type not
502                present or not recognisable at `exceptions.exceptions_dict`.
503            Other PumpWoodException sub-types:
504                If content-type is application/json if type is present and
505                recognisable.
506
507        Example:
508            No example
509        """
510        if not response.ok:
511            utcnow = datetime.datetime.now(datetime.UTC)
512            response_content_type = response.headers['content-type']
513
514            # Request information
515            url = response.url
516            method = response.request.method
517            if 'application/json' not in response_content_type.lower():
518                # Raise the exception as first in exception deep.
519                exception_dict = [{
520                    "exception_url": url,
521                    "exception_method": method,
522                    "exception_utcnow": utcnow.isoformat(),
523                    "exception_deep": 1}]
524                raise PumpWoodOtherException(
525                    message=response.text, payload={
526                        "!exception_stack!": exception_dict})
527
528            # Build error stack
529            response_dict = cls.angular_json(response)
530
531            # Removing previous error stack
532            payload = copy.deepcopy(
533                response_dict.get("payload", {}))
534            exception_stack = copy.deepcopy(
535                payload.pop("!exception_stack!", []))
536
537            exception_deep = len(exception_stack)
538            exception_dict = {
539                "exception_url": url,
540                "exception_method": method,
541                "exception_utcnow": utcnow.isoformat(),
542                "exception_deep": exception_deep + 1
543            }
544            exception_stack.insert(0, exception_dict)
545            payload["!exception_stack!"] = exception_stack
546
547            ###################
548            # Propagate error #
549            # get exception using 'type' key at response data and get the
550            # exception from exceptions_dict at exceptions
551            exception_message = response_dict.get("message", "")
552            exception_type = response_dict.get("type", None)
553            TempPumpwoodException = exceptions_dict.get(exception_type)
554            if TempPumpwoodException is not None:
555                raise TempPumpwoodException(
556                    message=exception_message,
557                    status_code=response.status_code,
558                    payload=payload)
559            else:
560                # If token is invalid is at response, return a
561                # PumpWoodUnauthorized error
562                is_invalid_token = cls.is_invalid_token_response(response)
563                response_dict["!exception_stack!"] = exception_stack
564                if is_invalid_token:
565                    raise PumpWoodUnauthorized(
566                        message="Invalid token", payload=payload)
567                else:
568                    # If the error is not mapped return a
569                    # PumpWoodOtherException limiting the message size to 1k
570                    # characters
571                    raise PumpWoodOtherException(
572                        message="Not mapped exception JSON",
573                        payload=response_dict)

Handle request error.

Check if is a Json and propagate the error with same type if possible. If not Json raises the content.

Arguments:
  • response: response to be handled, it is a PumpWoodException return it will raise the same exception at microservice object.
Returns:

No return.

Raises:
  • PumpWoodOtherException: If content-type is not application/json.
  • PumpWoodOtherException: If content-type is application/json, but type not present or not recognisable at exceptions.exceptions_dict.
  • Other PumpWoodException sub-types: If content-type is application/json if type is present and recognisable.
Example:

No example

def request_post( self, url: str, data: <built-in function any>, files: list = None, auth_header: dict = None, parameters: dict = {}) -> <built-in function any>:
722    def request_post(self, url: str, data: any, files: list = None,
723                     auth_header: dict = None,
724                     parameters: dict = {}) -> any:
725        """Make a POST a request to url with data as multipart/json payload.
726
727        Args:
728            url:
729                URL to make the request, already with server url.
730            data:
731                Data to be used as Json payload.
732            files:
733                A dictonary with file data, files will be set on field
734                corresponding.to dictonary key.
735                `{'file1': open('file1', 'rb'), {'file2': open('file2', 'rb')}`
736            parameters:
737                URL parameters.
738            auth_header:
739                AuthHeader to substitute the microservice original
740                at the request (user impersonation).
741
742        Returns:
743            Return the post response data.
744
745        Raises:
746            PumpWoodException sub-types:
747                Response is passed to error_handler.
748        """
749        post_url = urljoin(self.server_url, url)
750        dumped_parameters = self._dump_query_parameters(parameters=parameters)
751        response = None
752        if files is None:
753            response = self._request_post_json(
754                post_url=post_url, data=data, auth_header=auth_header,
755                parameters=dumped_parameters)
756        else:
757            response = self._request_post_multi(
758                post_url=post_url, data=data, files=files,
759                auth_header=auth_header, parameters=dumped_parameters)
760
761        # Handle errors and re-raise if Pumpwood Exceptions
762        self.error_handler(response)
763        return self._treat_response_for_file(response=response)

Make a POST a request to url with data as multipart/json payload.

Arguments:
  • url: URL to make the request, already with server url.
  • data: Data to be used as Json payload.
  • files: A dictonary with file data, files will be set on field corresponding.to dictonary key. {'file1': open('file1', 'rb'), {'file2': open('file2', 'rb')}
  • parameters: URL parameters.
  • auth_header: AuthHeader to substitute the microservice original at the request (user impersonation).
Returns:

Return the post response data.

Raises:
  • PumpWoodException sub-types: Response is passed to error_handler.
def request_get( self, url: str, parameters: dict = {}, auth_header: dict = None, use_disk_cache: bool = False, disk_cache_expire: int = None) -> Any:
765    def request_get(self, url: str, parameters: dict = {},
766                    auth_header: dict = None,
767                    use_disk_cache: bool = False,
768                    disk_cache_expire: int = None) -> Any:
769        """Make a GET a request to url with data as JSON payload.
770
771        Add the auth_header acording to login information and refresh token
772        if auth_header=None and object token is expired.
773
774        Args:
775            url (str):
776                URL to make the request.
777            parameters (dict):
778                URL parameters to make the request.
779            auth_header (dict):
780                Auth header to substitute the microservice original
781                at the request (user impersonation).
782            use_disk_cache (bool):
783                If set true, get request will use local cache to reduce
784                the requests to the backend.
785            disk_cache_expire (int):
786                Time in seconds to expire the cache, it None it will
787                use de default set be PumpwoodCache.
788
789        Returns:
790            Return the post reponse data.
791
792        Raises:
793            PumpWoodException sub-types:
794                Raise exception if reponse is not 2XX and if 'type' key on
795                JSON payload if found at exceptions_dict. Use the same
796                exception, message and payload.
797            PumpWoodOtherException:
798                If exception type is not found or return is not a json.
799        """
800        request_header = self._check_auth_header(auth_header)
801        # If is set to use diskcache, it will create a hash cash using
802        # the query paramerers, url and user access token. The
803        # hash will be used as index, not exposing the token at cache
804        # database
805        hash_dict = {}
806        if use_disk_cache:
807            hash_dict['authorization'] = request_header['Authorization']
808            hash_dict['parameters'] = parameters
809            hash_dict['url'] = url
810            cache_results = default_cache.get(hash_dict=hash_dict)
811            if cache_results is not None:
812                msg = "get from cache url[{url}]".format(url=url)
813                logger.info(msg)
814                return cache_results
815
816        dumped_parameters = self._dump_query_parameters(parameters=parameters)
817        get_url = urljoin(self.server_url, url)
818        response = requests.get(
819            get_url, verify=self._verify_ssl, headers=request_header,
820            params=dumped_parameters, timeout=self._default_timeout)
821
822        # If token is expired, refresh it
823        retry_with_login = (
824            self.is_invalid_token_response(response) and
825            auth_header is None)
826        if retry_with_login:
827            time.sleep(0.5)
828            self.login(force_refresh=True)
829            request_header = self._check_auth_header(auth_header=auth_header)
830            response = requests.get(
831                get_url, verify=self._verify_ssl, headers=request_header,
832                params=dumped_parameters, timeout=self._default_timeout)
833
834        # Re-raise Pumpwood exceptions
835        self.error_handler(response=response)
836        results = self._treat_response_for_file(response=response)
837
838        # If is set to use cache for this calls, set the local cache
839        if use_disk_cache and not results.get('__file__', False):
840            default_cache.set(
841                hash_dict=hash_dict, value=results,
842                expire=disk_cache_expire)
843        return results

Make a GET a request to url with data as JSON payload.

Add the auth_header acording to login information and refresh token if auth_header=None and object token is expired.

Arguments:
  • url (str): URL to make the request.
  • parameters (dict): URL parameters to make the request.
  • auth_header (dict): Auth header to substitute the microservice original at the request (user impersonation).
  • use_disk_cache (bool): If set true, get request will use local cache to reduce the requests to the backend.
  • disk_cache_expire (int): Time in seconds to expire the cache, it None it will use de default set be PumpwoodCache.
Returns:

Return the post reponse data.

Raises:
  • PumpWoodException sub-types: Raise exception if reponse is not 2XX and if 'type' key on JSON payload if found at exceptions_dict. Use the same exception, message and payload.
  • PumpWoodOtherException: If exception type is not found or return is not a json.
def request_delete(self, url, parameters: dict = None, auth_header: dict = None):
845    def request_delete(self, url, parameters: dict = None,
846                       auth_header: dict = None):
847        """Make a DELETE a request to url with data as Json payload.
848
849        Args:
850            url:
851                Url to make the request.
852            parameters:
853                Dictionary with Urls parameters.
854            auth_header:
855                Auth header to substitute the microservice original
856                at the request (user impersonation).
857
858        Returns:
859            Return the delete reponse payload.
860
861        Raises:
862            PumpWoodException sub-types:
863                Raise exception if reponse is not 2XX and if 'type' key on
864                JSON payload if found at exceptions_dict. Use the same
865                exception, message and payload.
866            PumpWoodOtherException:
867                If exception type is not found or return is not a json.
868        """
869        request_header = self._check_auth_header(auth_header)
870        dumped_parameters = self._dump_query_parameters(parameters=parameters)
871
872        post_url = self.server_url + url
873        response = requests.delete(
874            post_url, verify=self._verify_ssl, headers=request_header,
875            params=dumped_parameters, timeout=self._default_timeout)
876
877        # Retry request if token is not valid forcing token renew
878        retry_with_login = (
879            self.is_invalid_token_response(response) and
880            auth_header is None)
881        if retry_with_login:
882            time.sleep(0.5)
883            self.login(force_refresh=True)
884            request_header = self._check_auth_header(auth_header=auth_header)
885            response = requests.delete(
886                post_url, verify=self._verify_ssl, headers=request_header,
887                params=dumped_parameters, timeout=self._default_timeout)
888
889        # Re-raise Pumpwood Exceptions
890        self.error_handler(response)
891        return self.angular_json(response)

Make a DELETE a request to url with data as Json payload.

Arguments:
  • url: Url to make the request.
  • parameters: Dictionary with Urls parameters.
  • auth_header: Auth header to substitute the microservice original at the request (user impersonation).
Returns:

Return the delete reponse payload.

Raises:
  • PumpWoodException sub-types: Raise exception if reponse is not 2XX and if 'type' key on JSON payload if found at exceptions_dict. Use the same exception, message and payload.
  • PumpWoodOtherException: If exception type is not found or return is not a json.