1# Copyright 2014 Google Inc. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Errors for the library.
16
17All exceptions defined by the library
18should be defined in this file.
19"""
20from __future__ import absolute_import
21
22__author__ = "jcgregorio@google.com (Joe Gregorio)"
23
24import json
25
26from googleapiclient import _helpers as util
27
28
29class Error(Exception):
30    """Base error for this module."""
31
32    pass
33
34
35class HttpError(Error):
36    """HTTP data was invalid or unexpected."""
37
38    @util.positional(3)
39    def __init__(self, resp, content, uri=None):
40        self.resp = resp
41        if not isinstance(content, bytes):
42            raise TypeError("HTTP content should be bytes")
43        self.content = content
44        self.uri = uri
45        self.error_details = ""
46
47    def _get_reason(self):
48        """Calculate the reason for the error from the response content."""
49        reason = self.resp.reason
50        try:
51            data = json.loads(self.content.decode("utf-8"))
52            if isinstance(data, dict):
53                reason = data["error"]["message"]
54                if "details" in data["error"]:
55                    self.error_details = data["error"]["details"]
56                elif "detail" in data["error"]:
57                    self.error_details = data["error"]["detail"]
58            elif isinstance(data, list) and len(data) > 0:
59                first_error = data[0]
60                reason = first_error["error"]["message"]
61                if "details" in first_error["error"]:
62                    self.error_details = first_error["error"]["details"]
63        except (ValueError, KeyError, TypeError):
64            pass
65        if reason is None:
66            reason = ""
67        return reason
68
69    def __repr__(self):
70        reason = self._get_reason()
71        if self.error_details:
72            return '<HttpError %s when requesting %s returned "%s". Details: "%s">' % (
73                self.resp.status,
74                self.uri,
75                reason.strip(),
76                self.error_details,
77            )
78        elif self.uri:
79            return '<HttpError %s when requesting %s returned "%s">' % (
80                self.resp.status,
81                self.uri,
82                self._get_reason().strip(),
83            )
84        else:
85            return '<HttpError %s "%s">' % (self.resp.status, self._get_reason())
86
87    __str__ = __repr__
88
89
90class InvalidJsonError(Error):
91    """The JSON returned could not be parsed."""
92
93    pass
94
95
96class UnknownFileType(Error):
97    """File type unknown or unexpected."""
98
99    pass
100
101
102class UnknownLinkType(Error):
103    """Link type unknown or unexpected."""
104
105    pass
106
107
108class UnknownApiNameOrVersion(Error):
109    """No API with that name and version exists."""
110
111    pass
112
113
114class UnacceptableMimeTypeError(Error):
115    """That is an unacceptable mimetype for this operation."""
116
117    pass
118
119
120class MediaUploadSizeError(Error):
121    """Media is larger than the method can accept."""
122
123    pass
124
125
126class ResumableUploadError(HttpError):
127    """Error occured during resumable upload."""
128
129    pass
130
131
132class InvalidChunkSizeError(Error):
133    """The given chunksize is not valid."""
134
135    pass
136
137
138class InvalidNotificationError(Error):
139    """The channel Notification is invalid."""
140
141    pass
142
143
144class BatchError(HttpError):
145    """Error occured during batch operations."""
146
147    @util.positional(2)
148    def __init__(self, reason, resp=None, content=None):
149        self.resp = resp
150        self.content = content
151        self.reason = reason
152
153    def __repr__(self):
154        if getattr(self.resp, "status", None) is None:
155            return '<BatchError "%s">' % (self.reason)
156        else:
157            return '<BatchError %s "%s">' % (self.resp.status, self.reason)
158
159    __str__ = __repr__
160
161
162class UnexpectedMethodError(Error):
163    """Exception raised by RequestMockBuilder on unexpected calls."""
164
165    @util.positional(1)
166    def __init__(self, methodId=None):
167        """Constructor for an UnexpectedMethodError."""
168        super(UnexpectedMethodError, self).__init__(
169            "Received unexpected call %s" % methodId
170        )
171
172
173class UnexpectedBodyError(Error):
174    """Exception raised by RequestMockBuilder on unexpected bodies."""
175
176    def __init__(self, expected, provided):
177        """Constructor for an UnexpectedMethodError."""
178        super(UnexpectedBodyError, self).__init__(
179            "Expected: [%s] - Provided: [%s]" % (expected, provided)
180        )
181