Completed
Pull Request — master (#2895)
by Anthony
05:41
created

HTTPClient.patch()   A

Complexity

Conditions 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
from __future__ import absolute_import
17
18
import json
19
import logging
20
from pipes import quote as pquote
21
22
import requests
23
24
25
LOG = logging.getLogger(__name__)
26
27
28
def add_ssl_verify_to_kwargs(func):
29
    def decorate(*args, **kwargs):
30
        if isinstance(args[0], HTTPClient) and 'https' in getattr(args[0], 'root', ''):
31
            cacert = getattr(args[0], 'cacert', None)
32
            kwargs['verify'] = cacert if cacert is not None else False
33
        return func(*args, **kwargs)
34
    return decorate
35
36
37
def add_auth_token_to_headers(func):
38
    def decorate(*args, **kwargs):
39
        headers = kwargs.get('headers', dict())
40
41
        token = kwargs.pop('token', None)
42
        if token:
43
            headers['X-Auth-Token'] = str(token)
44
            kwargs['headers'] = headers
45
46
        api_key = kwargs.pop('api_key', None)
47
        if api_key:
48
            headers['St2-Api-Key'] = str(api_key)
49
            kwargs['headers'] = headers
50
51
        return func(*args, **kwargs)
52
    return decorate
53
54
55
def add_json_content_type_to_headers(func):
56
    def decorate(*args, **kwargs):
57
        headers = kwargs.get('headers', dict())
58
        content_type = headers.get('content-type', 'application/json')
59
        headers['content-type'] = content_type
60
        kwargs['headers'] = headers
61
        return func(*args, **kwargs)
62
    return decorate
63
64
65
class HTTPClient(object):
66
67
    def __init__(self, root, cacert=None, debug=False):
68
        self.root = self._get_url_without_trailing_slash(root)
69
        self.cacert = cacert
70
        self.debug = debug
71
72
    @add_ssl_verify_to_kwargs
73
    @add_auth_token_to_headers
74
    def get(self, url, **kwargs):
75
        response = requests.get(self.root + url, **kwargs)
76
        response = self._response_hook(response=response)
77
        return response
78
79
    @add_ssl_verify_to_kwargs
80
    @add_auth_token_to_headers
81
    @add_json_content_type_to_headers
82
    def post(self, url, data, **kwargs):
83
        response = requests.post(self.root + url, json.dumps(data), **kwargs)
84
        response = self._response_hook(response=response)
85
        return response
86
87
    @add_ssl_verify_to_kwargs
88
    @add_auth_token_to_headers
89
    def post_raw(self, url, data, **kwargs):
90
        response = requests.post(self.root + url, data, **kwargs)
91
        response = self._response_hook(response=response)
92
        return response
93
94
    @add_ssl_verify_to_kwargs
95
    @add_auth_token_to_headers
96
    @add_json_content_type_to_headers
97
    def put(self, url, data, **kwargs):
98
        response = requests.put(self.root + url, json.dumps(data), **kwargs)
99
        response = self._response_hook(response=response)
100
        return response
101
102
    @add_ssl_verify_to_kwargs
103
    @add_auth_token_to_headers
104
    @add_json_content_type_to_headers
105
    def patch(self, url, data, **kwargs):
106
        response = requests.patch(self.root + url, data, **kwargs)
107
        response = self._response_hook(response=response)
108
        return response
109
110
    @add_ssl_verify_to_kwargs
111
    @add_auth_token_to_headers
112
    def delete(self, url, **kwargs):
113
        response = requests.delete(self.root + url, **kwargs)
114
        response = self._response_hook(response=response)
115
        return response
116
117
    def _response_hook(self, response):
118
        if self.debug:
119
            # Log cURL request line
120
            curl_line = self._get_curl_line_for_request(request=response.request)
121
            print("# -------- begin %d request ----------" % id(self))
122
            print(curl_line)
123
            print("# -------- begin %d response ----------" % (id(self)))
124
            print(response.text)
125
            print("# -------- end %d response ------------" % (id(self)))
126
            print('')
127
128
        return response
129
130
    def _get_curl_line_for_request(self, request):
131
        parts = ['curl']
132
133
        # method
134
        method = request.method.upper()
135
        if method in ['HEAD']:
136
            parts.extend(['--head'])
137
        else:
138
            parts.extend(['-X', pquote(method)])
139
140
        # headers
141
        for key, value in request.headers.items():
142
            parts.extend(['-H ', pquote('%s: %s' % (key, value))])
143
144
        # body
145
        if request.body:
146
            parts.extend(['--data-binary', pquote(request.body)])
147
148
        # URL
149
        parts.extend([pquote(request.url)])
150
151
        curl_line = ' '.join(parts)
152
        return curl_line
153
154
    def _get_url_without_trailing_slash(self, value):
155
        """
156
        Function which strips a trailing slash from the provided url if one is present.
157
158
        :param value: URL to format.
159
        :type value: ``str``
160
161
        :rtype: ``str``
162
        """
163
        result = value[:-1] if value.endswith('/') else value
164
        return result
165