Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

''' 

# This return the api client. It should first check if requests is installed, if its installed then returns RequestsClient, 

# if requests is not installed then check for urlfetch lib, if its installed then return its client UrlfetchClient 

# if both of them not installed then throws the exception 

''' 

 

import textwrap 

from beyonic.errors import BeyonicError, ResponseError 

from beyonic.resources import GenericObject 

import os 

 

# let's try to import requests if its available 

try: 

    import requests 

except ImportError: 

    requests = None 

 

 

# let's try to import urlfetch 

try: 

    from google.appengine.api import urlfetch 

except ImportError: 

    urlfetch = None 

 

 

class BaseClient(object): 

    def __init__(self, verify_ssl_certs=True): 

        self._verify_ssl_certs = verify_ssl_certs 

 

 

class RequestsClient(BaseClient): 

    name = 'requests' 

 

    def request(self, method, url, headers, params=None): 

        if not requests: 

            raise BeyonicError('requests is not installed. Please install/setup it first using pip. e.g. pip install requests>=1.0') 

 

        kwargs = {} 

 

        if self._verify_ssl_certs: 

            kwargs['verify'] = True 

        else: 

            kwargs['verify'] = False 

 

        try: 

            try: 

                if method.upper() == 'GET': 

                    result = requests.request(method, 

                                          url, 

                                          headers=headers, 

                                          params=params, 

                                          timeout=80, 

                                          **kwargs) 

                else: 

                    result = requests.request(method, 

                                          url, 

                                          headers=headers, 

                                          data=params, 

                                          timeout=80, 

                                          **kwargs) 

            except TypeError, e: 

                raise TypeError( 

                    'Please upgrade your request library. The ' 

                    'underlying error was: %s' % (e,)) 

 

            content = result.content 

            status_code = result.status_code 

        except Exception, e: 

            # Would catch just requests.exceptions.RequestException, but can 

            # also raise ValueError, RuntimeError, etc. 

            self._handle_request_error(e) 

        return content, status_code 

 

    def _handle_request_error(self, e): 

        msg = ("Unexpected error communicating with Beyonic API.") 

        err = "A %s was raised" % (type(e).__name__,) 

        if str(e): 

            err += " with error message %s" % (str(e),) 

        else: 

            err += " with no error message" 

        msg = textwrap.fill(msg) + "\n\n(error: %s)" % (err,) 

        raise BeyonicError(msg) 

 

 

class UrlFetchClient(BaseClient): 

    name = 'urlfetch' 

 

    def request(self, method, url, headers, params=None): 

        if not urlfetch: 

            raise BeyonicError('urlfetch is not installed. Please install/setup it first.') 

        try: 

            result = urlfetch.fetch( 

                url=url, 

                method=method, 

                headers=headers, 

 

                validate_certificate=self._verify_ssl_certs, 

                deadline=55, 

                payload=params 

            ) 

        except urlfetch.Error, e: 

            self._handle_request_error(e, url) 

 

        return result.content, result.status_code 

 

    def _handle_request_error(self, e, url): 

        if isinstance(e, urlfetch.InvalidURLError): 

            msg = ("Unexpected error communicating with Beyonic API.") 

        elif isinstance(e, urlfetch.DownloadError): 

            msg = "There was a problem retrieving data from Beyonic." 

        elif isinstance(e, urlfetch.ResponseTooLargeError): 

            msg = ("Unexpected error communicating with Beyonic API.") 

        else: 

            msg = ("Unexpected error communicating with Beyonic API.") 

 

        msg = textwrap.fill(msg) + "\n\n(error: " + str(e) + ")" 

        raise BeyonicError(msg) 

 

 

def get_default_http_client(*args, **kwargs): 

    if urlfetch: 

        impl = UrlFetchClient 

    elif requests: 

        impl = RequestsClient 

    else: 

        # none of them is available so let's throw an error 

        raise BeyonicError( 

            'Either of requests or urlfetch is not installed. Please install either of them using requirements.txt') 

 

    return impl(*args, **kwargs) 

 

 

''' 

'API Client class interacts with api using available RequestClient or UrlFetchClient 

''' 

 

 

class ApiClient(object): 

    """ 

    A client for the api 

    """ 

    def __init__(self, api_key=None, url=None, client=None, verify_ssl_certs=True, api_version=None): 

        # if not passed then let's try to get it from env variable 

        if not api_key: 

            try: 

                api_key = os.environ['BEYONIC_ACCESS_KEY'] 

            except KeyError: 

                raise BeyonicError('BEYONIC_ACCESS_KEY not set.') 

 

        self._api_key = api_key 

        self._api_version = api_version 

        if not url: 

            raise BeyonicError('Base url can\'t be empty. You should set base url using beyonic.api_endpoint_base') 

 

        self._url = url 

        self._client = client or get_default_http_client(verify_ssl_certs=verify_ssl_certs) 

 

    def set_url(self, url): 

        self._url = url 

 

    def get(self, **kwargs): 

        ''' 

        Makes an HTTP GET request to the  API. Any keyword arguments will 

        be converted to query string parameters. 

        ''' 

        return self._request("get", **kwargs) 

 

    def post(self, **kwargs): 

        ''' 

        Makes an HTTP POST request to the  API. 

        ''' 

        return self._request("post", **kwargs) 

 

    def put(self, **kwargs): 

        ''' 

        Makes an HTTP PUT request to the  API. 

        ''' 

        return self._request("put", **kwargs) 

 

    def patch(self, **kwargs): 

        ''' 

        Makes an HTTP patch request to the  API. 

        ''' 

        return self._request("patch", **kwargs) 

 

    def delete(self, **kwargs): 

        ''' 

        Makes an HTTP DELETE request to the  API. 

        ''' 

        return self._request("delete", **kwargs) 

 

    def _request(self, method, **kwargs): 

        response_content, status_code = self._client.request( 

            method=method, 

            url=self._url, 

            headers=self._build_headers(), 

            params=kwargs, 

        ) 

        return self._parse_response(response_content, status_code) 

 

    def _build_headers(self): 

        headers = {} 

        if self._api_key: 

            headers.update({"Authorization": "Token %s" % self._api_key, }) 

 

        if self._api_version: 

            headers.update({"Beyonic-Version": self._api_version, }) 

 

        return headers 

 

    def _parse_response(self, response_content, status_code): 

        # TODO: need exact status code for different type error 

        if 200 <= status_code < 300: 

            return self._value_for_response(response_content, status_code) 

        else: 

            raise self._exception_for_response(response_content, status_code) 

 

    def _value_for_response(self, response_content, status_code): 

        #status_code 204 is for delete so for it retruning True 

        if response_content and status_code != 204: 

            return GenericObject.from_json(response_content) 

        else: 

            return True 

 

    def _exception_for_response(self, response_content, status_code): 

        # TODO need to handle the all the status 

        return ResponseError("%d error: %s" % (status_code, response_content,))