Skip to content

Commit

Permalink
feat: error-doc handling in python SDK (#1440)
Browse files Browse the repository at this point in the history
Lookups now take place against the CDN to find a more detailed error
guide. The url to that error guide, and the error guide itself - a
markdown document - are included in the SDKError class and the
ErrorDetail class.
  • Loading branch information
drstrangelooker committed Mar 8, 2024
1 parent 62b3d56 commit 21daada
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 12 deletions.
117 changes: 106 additions & 11 deletions python/looker_sdk/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,128 @@
# THE SOFTWARE.

import attr
from typing import Optional, Sequence
from typing import cast, Dict, Optional, Sequence, Tuple
import requests
import json
import re

"""API error class
"""


@attr.s(auto_attribs=True, kw_only=True)
class ErrorDetail():
class ErrorDetail:
"""Error detail:
documentation_url: documentation link
field: field with error
code: error code
message: error info message
documentation_url: documentation link
field: field with error
code: error code
message: error info message
error_doc_url: URL that may point to additional useful information
error_doc: Markdown doc that may contain additional useful information
"""

documentation_url: str
field: Optional[str] = ""
code: Optional[str] = ""
message: Optional[str] = ""
error_doc_url: str = ""
error_doc: str = ""

def __str__(self):
return f"""
*****
documentation_url: {self.documentation_url}
field: {self.field}
code: {self.code}
message: {self.message}
error_doc_url: {self.error_doc_url}
"""


@attr.s(auto_attribs=True)
class SDKError(Exception):
"""API error class:
message: main error info message
errors: array of error details
documentation_url: documentation link
message: main error info message
errors: array of error details
documentation_url: documentation link
error_doc_url: URL that may point to additional useful information
error_doc: Markdown doc that may contain additional useful information
"""

message: str
errors: Optional[Sequence[ErrorDetail]] = attr.ib(default=[], kw_only=True)
documentation_url: Optional[str] = attr.ib(default="", kw_only=True)
errors: Sequence[ErrorDetail] = attr.ib(default=[], kw_only=True)
documentation_url: str = attr.ib(default="", kw_only=True)
error_doc_url: str = ""
error_doc: str = ""

def __str__(self):
sep = "****\n"
return f"""
message: {self.message}
documentation_url: {self.documentation_url}
error_doc_url: {self.error_doc_url}
error details:
{sep.join(self.errors)}
"""


"""Error Doc Helper class
"""


@attr.s(auto_attribs=True, kw_only=True)
class ErrorDocHelper:
"""Error Doc Helper:
error_doc_url: link
"""

ERROR_CODES_URL: str = "https://static-a.cdn.looker.app/errorcodes/"
lookup_dict: Dict[str, Dict[str, str]] = {}
RE_PATTERN: str = (
"""(https://docs\.looker\.com/r/err/|https://cloud\.google\.com/looker/docs/r/err/)(.*)/(\d{3})(.*)"""
)
pattern = re.compile(RE_PATTERN, flags=re.IGNORECASE)

def get_index(self, url: str = ERROR_CODES_URL) -> None:
r = requests.get(f"{url}index.json")
self.lookup_dict = json.loads(r.text)

def lookup(
self, url: str = ERROR_CODES_URL, code: str = "", path: str = ""
) -> Tuple[str, str]:
if len(self.lookup_dict) == 0:
self.get_index(url=url)

error_doc_url: str = ""
error_doc: str = ""
if path:
try:
error_doc_url = self.lookup_dict[f"{code}{path}"]["url"]
except KeyError:
error_doc = f"### No documentation found for {code}{path}"
if not error_doc_url:
try:
error_doc_url = self.lookup_dict[code]["url"]
except KeyError:
if not error_doc:
error_doc = f"### No documentation found for {code}"

if error_doc_url:
r = requests.get(f"{self.ERROR_CODES_URL}{error_doc_url}")
error_doc = r.text

return (f"{self.ERROR_CODES_URL}{error_doc_url}", error_doc)

def parse_and_lookup(
self, error_url: str, url: str = ERROR_CODES_URL
) -> Tuple[str, str]:
m = re.search(self.RE_PATTERN, error_url)
if not m:
return ("", "")

code: str = cast(Tuple[str, str, str, str], m.groups())[2]
path: str = cast(Tuple[str, str, str, str], m.groups())[3]
try:
return self.lookup(url=url, code=code, path=path)
except requests.exceptions.RequestException:
return ("", "")
10 changes: 9 additions & 1 deletion python/looker_sdk/rtl/api_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,15 @@ def _return(self, response: transport.Response, structure: TStructure) -> TRetur
value = response.value.decode(encoding=encoding)
sdk_error: error.SDKError
try:
sdk_error = self.deserialize(data=value, structure=error.SDKError) # type: ignore
sdk_error = self.deserialize(data=value, structure=error.SDKError) # type: ignore
helper = error.ErrorDocHelper()
(sdk_error.error_doc_url, sdk_error.error_doc) = (
helper.parse_and_lookup(sdk_error.documentation_url)
)
for e in sdk_error.errors:
(e.error_doc_url, e.error_doc) = helper.parse_and_lookup(
e.documentation_url
)
except serialize.DeserializeError:
raise error.SDKError(value)
raise sdk_error
Expand Down

0 comments on commit 21daada

Please sign in to comment.