-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathgroups.py
More file actions
182 lines (152 loc) · 6.85 KB
/
groups.py
File metadata and controls
182 lines (152 loc) · 6.85 KB
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
import difflib
import platform
import re
from collections import OrderedDict
import click
from py42.exceptions import Py42ActiveLegalHoldError
from py42.exceptions import Py42CaseAlreadyHasEventError
from py42.exceptions import Py42CaseNameExistsError
from py42.exceptions import Py42CloudAliasCharacterLimitExceededError
from py42.exceptions import Py42CloudAliasLimitExceededError
from py42.exceptions import Py42DescriptionLimitExceededError
from py42.exceptions import Py42ForbiddenError
from py42.exceptions import Py42HTTPError
from py42.exceptions import Py42InvalidEmailError
from py42.exceptions import Py42InvalidPageTokenError
from py42.exceptions import Py42InvalidPasswordError
from py42.exceptions import Py42InvalidRuleOperationError
from py42.exceptions import Py42InvalidUsernameError
from py42.exceptions import Py42LegalHoldNotFoundOrPermissionDeniedError
from py42.exceptions import Py42NotFoundError
from py42.exceptions import Py42OrgNotFoundError
from py42.exceptions import Py42TrustedActivityConflictError
from py42.exceptions import Py42TrustedActivityIdNotFound
from py42.exceptions import Py42TrustedActivityInvalidCharacterError
from py42.exceptions import Py42UpdateClosedCaseError
from py42.exceptions import Py42UserAlreadyAddedError
from py42.exceptions import Py42UsernameMustBeEmailError
from py42.exceptions import Py42UserNotOnListError
from py42.exceptions import Py42UserRiskProfileNotFound
from py42.exceptions import Py42WatchlistNotFound
from code42cli.errors import Code42CLIError
from code42cli.errors import LoggedCLIError
from code42cli.errors import UserDoesNotExistError
from code42cli.logger import get_main_cli_logger
from code42cli.logger.handlers import SyslogServerNetworkConnectionError
_DIFFLIB_CUT_OFF = 0.6
class ExceptionHandlingGroup(click.Group):
"""A `click.Group` subclass to add custom exception handling."""
logger = get_main_cli_logger()
_original_args = None
def make_context(self, info_name, args, parent=None, **extra):
# grab the original command line arguments for logging purposes
self._original_args = " ".join(args)
return super().make_context(info_name, args, parent=parent, **extra)
def invoke(self, ctx):
try:
return super().invoke(ctx)
except click.UsageError as err:
self._suggest_cmd(err)
except LoggedCLIError:
raise
except Code42CLIError as err:
self.logger.log_error(str(err))
raise
except click.ClickException:
raise
except click.exceptions.Exit:
raise
except (
UserDoesNotExistError,
Py42UserAlreadyAddedError,
Py42UserNotOnListError,
Py42InvalidRuleOperationError,
Py42LegalHoldNotFoundOrPermissionDeniedError,
SyslogServerNetworkConnectionError,
Py42CaseNameExistsError,
Py42DescriptionLimitExceededError,
Py42CaseAlreadyHasEventError,
Py42UpdateClosedCaseError,
Py42UsernameMustBeEmailError,
Py42InvalidEmailError,
Py42InvalidPageTokenError,
Py42InvalidPasswordError,
Py42InvalidUsernameError,
Py42ActiveLegalHoldError,
Py42OrgNotFoundError,
Py42TrustedActivityConflictError,
Py42TrustedActivityInvalidCharacterError,
Py42TrustedActivityIdNotFound,
Py42CloudAliasLimitExceededError,
Py42CloudAliasCharacterLimitExceededError,
Py42UserRiskProfileNotFound,
Py42WatchlistNotFound,
Py42NotFoundError,
) as err:
msg = err.args[0]
self.logger.log_error(msg)
raise Code42CLIError(msg)
except Py42ForbiddenError as err:
self.logger.log_verbose_error(self._original_args, err.response.request)
raise LoggedCLIError(
"You do not have the necessary permissions to perform this task. "
"Try using or creating a different profile."
)
except Py42HTTPError as err:
self.logger.log_verbose_error(self._original_args, err.response.request)
raise LoggedCLIError("Problem making request to server.")
except UnicodeEncodeError:
if platform.system() == "Windows":
cmd = 'if using powershell: $ENV:PYTHONIOENCODING="utf-16"\nif using cmd.exe: SET PYTHONIOENCODING="utf-16"'
else:
cmd = 'export PYTHONIOENCODING="utf-8"'
raise Code42CLIError(
f"Failed to handle unicode character using environment's detected encoding, try running the following:\n\n{cmd}\n\nand then re-run your `code42` command."
)
except OSError:
raise
except Exception:
self.logger.log_verbose_error()
raise LoggedCLIError("Unknown problem occurred.")
@staticmethod
def _suggest_cmd(usage_err):
"""Handles fuzzy suggestion of commands that are close to the bad command entered."""
if usage_err.message is not None:
match = re.match("No such command '(.*)'.", usage_err.message)
if match:
bad_arg = match.groups()[0]
available_commands = list(usage_err.ctx.command.commands.keys())
suggested_commands = difflib.get_close_matches(
bad_arg, available_commands, cutoff=_DIFFLIB_CUT_OFF
)
if not suggested_commands:
raise usage_err
usage_err.message = (
f"No such command '{bad_arg}'. "
f"Did you mean {' or '.join(suggested_commands)}?"
)
raise usage_err
class OrderedGroup(click.Group):
"""A `click.Group` subclass that uses an `OrderedDict` to store commands so the help text lists
them in the order they were defined/added to the group.
"""
def __init__(self, name=None, commands=None, **attrs):
super().__init__(name, commands, **attrs)
# the registered subcommands by their exported names.
self.commands = commands or OrderedDict()
def list_commands(self, ctx):
return self.commands
class ExtensionGroup(ExceptionHandlingGroup):
"""A helper click.Group for extension scripts. If only a single command is added to this group,
that command will be the "default" and won't need to be explicitly passed as the first argument
to the extension script.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def parse_args(self, ctx, args):
if len(self.commands) == 1:
cmd_name, cmd = next(iter(self.commands.items()))
if not args or args[0] not in self.commands:
self.commands = {"": cmd}
args.insert(0, "")
super().parse_args(ctx, args)