Skip to content

Commit a42a9fb

Browse files
committed
refactor(user): describe as @click.group of commands
1 parent d898180 commit a42a9fb

20 files changed

+427
-597
lines changed
+24-18
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
1-
from argparse import Namespace
1+
import click
22

3-
from compiler_admin.commands.user.alumni import alumni # noqa: F401
4-
from compiler_admin.commands.user.create import create # noqa: F401
5-
from compiler_admin.commands.user.convert import convert # noqa: F401
6-
from compiler_admin.commands.user.delete import delete # noqa: F401
7-
from compiler_admin.commands.user.offboard import offboard # noqa: F401
8-
from compiler_admin.commands.user.reset import reset # noqa: F401
9-
from compiler_admin.commands.user.restore import restore # noqa: F401
10-
from compiler_admin.commands.user.signout import signout # noqa: F401
3+
from compiler_admin.commands.user.alumni import alumni
4+
from compiler_admin.commands.user.convert import convert
5+
from compiler_admin.commands.user.create import create
6+
from compiler_admin.commands.user.delete import delete
7+
from compiler_admin.commands.user.offboard import offboard
8+
from compiler_admin.commands.user.reset import reset
9+
from compiler_admin.commands.user.restore import restore
10+
from compiler_admin.commands.user.signout import signout
1111

1212

13-
def user(args: Namespace, *extra):
14-
# try to call the subcommand function directly from global (module) symbols
15-
# if the subcommand function was imported above, it should exist in globals()
16-
global_env = globals()
13+
@click.group
14+
def user():
15+
"""
16+
Work with users in the Compiler org.
17+
"""
18+
pass
1719

18-
if args.subcommand in global_env:
19-
cmd_func = global_env[args.subcommand]
20-
cmd_func(args, *extra)
21-
else:
22-
raise NotImplementedError(f"Unknown user subcommand: {args.subcommand}")
20+
21+
user.add_command(alumni)
22+
user.add_command(convert)
23+
user.add_command(create)
24+
user.add_command(delete)
25+
user.add_command(offboard)
26+
user.add_command(reset)
27+
user.add_command(restore)
28+
user.add_command(signout)
+44-87
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,73 @@
1-
from argparse import Namespace
1+
import click
22

33
from compiler_admin import RESULT_FAILURE, RESULT_SUCCESS
44
from compiler_admin.commands.user.reset import reset
55
from compiler_admin.services.google import (
66
OU_ALUMNI,
7-
USER_HELLO,
87
CallGAMCommand,
98
move_user_ou,
109
user_account_name,
1110
user_exists,
1211
)
1312

1413

15-
def alumni(args: Namespace) -> int:
16-
"""Convert a user to a Compiler alumni.
17-
18-
Optionally notify an email address with the new randomly generated password.
19-
20-
Args:
21-
username (str): the user account to convert.
22-
23-
notify (str): an email address to send the new password notification.
24-
Returns:
25-
A value indicating if the operation succeeded or failed.
14+
@click.command()
15+
@click.option("-f", "--force", is_flag=True, help="Don't ask for confirmation.")
16+
@click.option("-n", "--notify", help="An email address to send the new password notification.")
17+
@click.option(
18+
"-e",
19+
"--recovery-email",
20+
help="An email address to use as the new recovery email. Without a value, clears the recovery email.",
21+
)
22+
@click.option(
23+
"-p",
24+
"--recovery-phone",
25+
help="A phone number to use as the new recovery phone number. Without a value, clears the recovery phone number.",
26+
)
27+
@click.argument("username")
28+
@click.pass_context
29+
def alumni(
30+
ctx: click.Context, username: str, force: bool = False, recovery_email: str = "", recovery_phone: str = "", **kwargs
31+
):
2632
"""
27-
if not hasattr(args, "username"):
28-
raise ValueError("username is required")
29-
30-
account = user_account_name(args.username)
33+
Convert a user to a Compiler alumni.
34+
"""
35+
account = user_account_name(username)
3136

3237
if not user_exists(account):
33-
print(f"User does not exist: {account}")
34-
return RESULT_FAILURE
38+
click.echo(f"User does not exist: {account}")
39+
raise SystemExit(RESULT_FAILURE)
3540

36-
if getattr(args, "force", False) is False:
37-
cont = input(f"Convert account to alumni: {account}? (Y/n) ")
41+
if not force:
42+
cont = input(f"Convert account to alumni for {account}? (Y/n): ")
3843
if not cont.lower().startswith("y"):
39-
print("Aborting conversion.")
40-
return RESULT_SUCCESS
44+
click.echo("Aborting conversion.")
45+
raise SystemExit(RESULT_SUCCESS)
4146

42-
res = RESULT_SUCCESS
47+
click.echo(f"User exists, converting to alumni: {account}")
4348

44-
print("Removing from groups")
45-
res += CallGAMCommand(("user", account, "delete", "groups"))
49+
click.echo("Removing from groups")
50+
CallGAMCommand(("user", account, "delete", "groups"))
4651

47-
print(f"Moving to OU: {OU_ALUMNI}")
48-
res += move_user_ou(account, OU_ALUMNI)
52+
click.echo(f"Moving to OU: {OU_ALUMNI}")
53+
move_user_ou(account, OU_ALUMNI)
4954

5055
# reset password, sign out
51-
res += reset(args)
56+
ctx.forward(reset)
5257

53-
print("Clearing user profile info")
58+
click.echo("Clearing user profile info")
5459
for prop in ["address", "location", "otheremail", "phone"]:
5560
command = ("update", "user", account, prop, "clear")
56-
res += CallGAMCommand(command)
61+
CallGAMCommand(command)
5762

58-
print("Resetting recovery email")
59-
recovery = getattr(args, "recovery_email", "")
60-
command = ("update", "user", account, "recoveryemail", recovery)
61-
res += CallGAMCommand(command)
63+
click.echo("Resetting recovery email")
64+
command = ("update", "user", account, "recoveryemail", recovery_email)
65+
CallGAMCommand(command)
6266

63-
print("Resetting recovery phone")
64-
recovery = getattr(args, "recovery_phone", "")
65-
command = ("update", "user", account, "recoveryphone", recovery)
66-
res += CallGAMCommand(command)
67+
click.echo("Resetting recovery phone")
68+
command = ("update", "user", account, "recoveryphone", recovery_phone)
69+
CallGAMCommand(command)
6770

68-
print("Turning off 2FA")
71+
click.echo("Turning off 2FA")
6972
command = ("user", account, "turnoff2sv")
70-
res += CallGAMCommand(command)
71-
72-
print("Resetting email signature")
73-
# https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Gmail-Send-As-Signature-Vacation#manage-signature
74-
command = (
75-
"user",
76-
account,
77-
"signature",
78-
f"Compiler LLC<br />https://compiler.la<br />{USER_HELLO}",
79-
"replyto",
80-
USER_HELLO,
81-
"default",
82-
"treatasalias",
83-
"false",
84-
"name",
85-
"Compiler LLC",
86-
"primary",
87-
)
88-
res += CallGAMCommand(command)
89-
90-
print("Turning on email autoresponder")
91-
# https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Gmail-Send-As-Signature-Vacation#manage-vacation
92-
message = (
93-
"Thank you for contacting Compiler. This inbox is no longer actively monitored.<br /><br />"
94-
+ f"Please reach out to {USER_HELLO} if you need to get a hold of us."
95-
)
96-
command = (
97-
"user",
98-
account,
99-
"vacation",
100-
"true",
101-
"subject",
102-
"[This inbox is no longer active]",
103-
"message",
104-
message,
105-
"contactsonly",
106-
"false",
107-
"domainonly",
108-
"false",
109-
"start",
110-
"Started",
111-
"end",
112-
"2999-12-31",
113-
)
114-
res += CallGAMCommand(command)
115-
116-
return res
73+
CallGAMCommand(command)
+42-41
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from argparse import Namespace
1+
import click
22

3-
from compiler_admin import RESULT_SUCCESS, RESULT_FAILURE
3+
from compiler_admin import RESULT_FAILURE
44
from compiler_admin.commands.user.alumni import alumni
55
from compiler_admin.services.google import (
66
GROUP_PARTNERS,
@@ -22,62 +22,63 @@
2222
ACCOUNT_TYPE_OU = {"alumni": OU_ALUMNI, "contractor": OU_CONTRACTORS, "partner": OU_PARTNERS, "staff": OU_STAFF}
2323

2424

25-
def convert(args: Namespace) -> int:
26-
f"""Convert a user of one type to another.
27-
Args:
28-
username (str): The account to convert. Must exist already.
29-
30-
account_type (str): One of {", ".join(ACCOUNT_TYPE_OU.keys())}
31-
Returns:
32-
A value indicating if the operation succeeded or failed.
25+
@click.command()
26+
@click.option("-f", "--force", is_flag=True, help="Don't ask for confirmation.")
27+
@click.option(
28+
"-n", "--notify", help="An email address to send the new password notification. Only valid for alumni conversion."
29+
)
30+
@click.option(
31+
"-e",
32+
"--recovery-email",
33+
help="An email address to use as the new recovery email. Only valid for alumni conversion.",
34+
)
35+
@click.option(
36+
"-p",
37+
"--recovery-phone",
38+
help="A phone number to use as the new recovery phone number. Only valid for alumni conversion.",
39+
)
40+
@click.argument("username")
41+
@click.argument("account_type", type=click.Choice(ACCOUNT_TYPE_OU.keys(), case_sensitive=False))
42+
@click.pass_context
43+
def convert(ctx: click.Context, username: str, account_type: str, **kwargs):
3344
"""
34-
if not hasattr(args, "username"):
35-
raise ValueError("username is required")
36-
if not hasattr(args, "account_type"):
37-
raise ValueError("account_type is required")
38-
39-
account = user_account_name(args.username)
40-
account_type = args.account_type
45+
Convert a user of one type to another.
46+
"""
47+
account = user_account_name(username)
4148

4249
if not user_exists(account):
43-
print(f"User does not exist: {account}")
44-
return RESULT_FAILURE
50+
click.echo(f"User does not exist: {account}")
51+
raise SystemExit(RESULT_FAILURE)
4552

46-
if account_type not in ACCOUNT_TYPE_OU:
47-
print(f"Unknown account type for conversion: {account_type}")
48-
return RESULT_FAILURE
49-
50-
print(f"User exists, converting to: {account_type} for {account}")
51-
res = RESULT_SUCCESS
53+
click.echo(f"User exists, converting to: {account_type} for {account}")
5254

5355
if account_type == "alumni":
54-
res = alumni(args)
56+
# call the alumni command
57+
ctx.forward(alumni)
5558

5659
elif account_type == "contractor":
5760
if user_is_partner(account):
58-
res += remove_user_from_group(account, GROUP_PARTNERS)
59-
res += remove_user_from_group(account, GROUP_STAFF)
61+
remove_user_from_group(account, GROUP_PARTNERS)
62+
remove_user_from_group(account, GROUP_STAFF)
6063
elif user_is_staff(account):
61-
res = remove_user_from_group(account, GROUP_STAFF)
64+
remove_user_from_group(account, GROUP_STAFF)
6265

6366
elif account_type == "staff":
6467
if user_is_partner(account):
65-
res += remove_user_from_group(account, GROUP_PARTNERS)
68+
remove_user_from_group(account, GROUP_PARTNERS)
6669
elif user_is_staff(account):
67-
print(f"User is already staff: {account}")
68-
return RESULT_FAILURE
69-
res += add_user_to_group(account, GROUP_STAFF)
70+
click.echo(f"User is already staff: {account}")
71+
raise SystemExit(RESULT_FAILURE)
72+
add_user_to_group(account, GROUP_STAFF)
7073

7174
elif account_type == "partner":
7275
if user_is_partner(account):
73-
print(f"User is already partner: {account}")
74-
return RESULT_FAILURE
76+
click.echo(f"User is already partner: {account}")
77+
raise SystemExit(RESULT_FAILURE)
7578
if not user_is_staff(account):
76-
res += add_user_to_group(account, GROUP_STAFF)
77-
res += add_user_to_group(account, GROUP_PARTNERS)
78-
79-
res += move_user_ou(account, ACCOUNT_TYPE_OU[account_type])
79+
add_user_to_group(account, GROUP_STAFF)
80+
add_user_to_group(account, GROUP_PARTNERS)
8081

81-
print(f"Account conversion complete for: {account}")
82+
move_user_ou(account, ACCOUNT_TYPE_OU[account_type])
8283

83-
return RESULT_SUCCESS if res == RESULT_SUCCESS else RESULT_FAILURE
84+
click.echo(f"Account conversion complete for: {account}")
+29-30
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,46 @@
1-
from argparse import Namespace
2-
from typing import Sequence
3-
4-
from compiler_admin import RESULT_SUCCESS, RESULT_FAILURE
5-
from compiler_admin.services.google import GROUP_TEAM, add_user_to_group, CallGAMCommand, user_account_name, user_exists
6-
7-
8-
def create(args: Namespace, *extra: Sequence[str]) -> int:
9-
"""Create a new user account in Compiler.
1+
import click
2+
3+
from compiler_admin import RESULT_FAILURE
4+
from compiler_admin.services.google import (
5+
GROUP_TEAM,
6+
USER_HELLO,
7+
add_user_to_group,
8+
CallGAMCommand,
9+
user_account_name,
10+
user_exists,
11+
)
12+
13+
14+
@click.command(context_settings={"ignore_unknown_options": True})
15+
@click.option("-n", "--notify", help="An email address to send the new password notification.")
16+
@click.argument("username")
17+
@click.argument("gam_args", nargs=-1, type=click.UNPROCESSED)
18+
def create(username: str, notify: str = "", gam_args: list = []):
19+
"""Create a new user account.
1020
1121
The user's password is randomly generated and requires reset on first login.
1222
1323
Extra args are passed along to GAM as options.
14-
See https://github.com/taers232c/GAMADV-XTD3/wiki/Users#create-a-user
15-
for the complete list of options supported.
1624
17-
Args:
18-
username (str): The account to create. Must not exist already.
19-
Returns:
20-
A value indicating if the operation succeeded or failed.
25+
https://github.com/taers232c/GAMADV-XTD3/wiki/Users#create-a-user
2126
"""
22-
if not hasattr(args, "username"):
23-
raise ValueError("username is required")
24-
25-
account = user_account_name(args.username)
27+
account = user_account_name(username)
2628

2729
if user_exists(account):
28-
print(f"User already exists: {account}")
29-
return RESULT_FAILURE
30+
click.echo(f"User already exists: {account}")
31+
raise SystemExit(RESULT_FAILURE)
3032

31-
print(f"User does not exist, continuing: {account}")
33+
click.echo(f"User does not exist, continuing: {account}")
3234

3335
command = ("create", "user", account, "password", "random", "changepassword")
3436

35-
notify = getattr(args, "notify", None)
3637
if notify:
37-
command += ("notify", notify, "from", "[email protected]")
38-
39-
command += (*extra,)
38+
command += ("notify", notify, "from", USER_HELLO)
4039

41-
res = CallGAMCommand(command)
40+
command += (*gam_args,)
4241

43-
res += add_user_to_group(account, GROUP_TEAM)
42+
CallGAMCommand(command)
4443

45-
print(f"User created successfully: {account}")
44+
add_user_to_group(account, GROUP_TEAM)
4645

47-
return RESULT_SUCCESS if res == RESULT_SUCCESS else RESULT_FAILURE
46+
click.echo(f"User created successfully: {account}")

0 commit comments

Comments
 (0)