Skip to content

Commit 477fb12

Browse files
kdailyryansonshine
andauthored
[v2] Add profile and role sorting to sso configure (#10149)
This change adds sorting to accounts and roles when running `aws sso configure`. Accounts are sorted in case insensitive, ascending alphabetical order using (in order of precedence) account name, email address, and account ID. Roles are sorted in case insensitive ascending alphabetical order. Co-authored-by: Ryan Sonshine <ryansonshine@users.noreply.github.com>
1 parent e7fe660 commit 477fb12

3 files changed

Lines changed: 205 additions & 7 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "enhancement",
3+
"category": "``sso configure``",
4+
"description": "Add sorting to accounts and roles `#6108 <https://github.com/aws/aws-cli/issues/6108>`__"
5+
}

awscli/customizations/configure/sso.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,15 @@ def display_account(account):
186186
return account_template.format(**account)
187187

188188

189+
def get_account_sorting_key(account):
190+
only_account_id = ('accountName' not in account and 'emailAddress' not in account)
191+
for key in ('accountName', 'emailAddress', 'accountId'):
192+
value = account.get(key, None)
193+
if value is not None:
194+
return (only_account_id, value.lower())
195+
return (only_account_id, None)
196+
197+
189198
class SSOSessionConfigurationPrompter:
190199
_DEFAULT_SSO_SCOPE = 'sso:account:access'
191200
_KNOWN_SSO_SCOPES = {
@@ -441,8 +450,9 @@ def _handle_multiple_accounts(self, accounts):
441450
'There are {} AWS accounts available to you.\n'
442451
)
443452
uni_print(available_accounts_msg.format(len(accounts)))
453+
sorted_accounts = sorted(accounts, key=get_account_sorting_key)
444454
selected_account = self._selector(
445-
accounts, display_format=display_account
455+
sorted_accounts, display_format=display_account
446456
)
447457
sso_account_id = selected_account['accountId']
448458
return sso_account_id
@@ -473,7 +483,8 @@ def _handle_single_role(self, roles):
473483
def _handle_multiple_roles(self, roles):
474484
available_roles_msg = 'There are {} roles available to you.\n'
475485
uni_print(available_roles_msg.format(len(roles)))
476-
role_names = [r['roleName'] for r in roles]
486+
sorted_roles = sorted(roles, key=lambda x: x['roleName'].lower())
487+
role_names = [r['roleName'] for r in sorted_roles]
477488
sso_role_name = self._selector(role_names)
478489
return sso_role_name
479490

tests/unit/customizations/configure/test_sso.py

Lines changed: 187 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
SSOSessionConfigurationPrompter,
3838
StartUrlValidator,
3939
display_account,
40+
get_account_sorting_key,
4041
)
4142
from awscli.customizations.sso.utils import (
4243
PrintOnlyHandler,
@@ -359,18 +360,57 @@ def account_id_select(account_id):
359360
}
360361
return SelectMenu(
361362
answer=selected_account,
362-
expected_choices=[
363-
selected_account,
364-
{"accountId": "1234567890", "emailAddress": "account2@site.com"},
365-
],
363+
expected_choices=sorted(
364+
[
365+
selected_account,
366+
{"accountId": "1234567890", "emailAddress": "account2@site.com"},
367+
],
368+
key=get_account_sorting_key,
369+
)
366370
)
367371

368-
369372
@pytest.fixture
370373
def role_name_select(role_name):
371374
return SelectMenu(answer=role_name, expected_choices=[role_name, "roleB"])
372375

373376

377+
@pytest.fixture
378+
def stub_account_sorting_flow(
379+
ptk_stubber,
380+
stub_sso_list_accounts,
381+
stub_sso_list_roles,
382+
start_url_prompt,
383+
sso_region_prompt,
384+
region_prompt,
385+
output_prompt,
386+
profile_prompt,
387+
account_id,
388+
role_name,
389+
):
390+
def _stub(accounts, expected_sorted_accounts, selected_account):
391+
account_select = SelectMenu(
392+
answer=selected_account,
393+
expected_choices=expected_sorted_accounts,
394+
)
395+
ptk_stubber.user_inputs = UserInputs(
396+
session_prompt=RecommendedSessionPrompt(answer=""),
397+
start_url_prompt=start_url_prompt,
398+
sso_region_prompt=sso_region_prompt,
399+
account_id_select=account_select,
400+
role_name_select=None,
401+
region_prompt=region_prompt,
402+
output_prompt=output_prompt,
403+
profile_prompt=profile_prompt,
404+
)
405+
stub_sso_list_accounts(accounts)
406+
stub_sso_list_roles(
407+
[role_name],
408+
expected_account_id=account_id,
409+
)
410+
411+
return _stub
412+
413+
374414
@pytest.fixture
375415
def region_prompt():
376416
return RegionPrompt(answer="us-west-2", expected_default=None)
@@ -1524,6 +1564,146 @@ def test_single_account_single_role_device_code_fallback(
15241564
],
15251565
)
15261566

1567+
def test_account_list_sorted_by_name(
1568+
self,
1569+
sso_cmd,
1570+
stub_account_sorting_flow,
1571+
args,
1572+
parsed_globals,
1573+
account_id,
1574+
):
1575+
selected = {
1576+
'accountId': account_id,
1577+
'accountName': 'Charlie',
1578+
'emailAddress': 'charlie@example.com',
1579+
}
1580+
first = {
1581+
'accountId': '1111111111',
1582+
'accountName': 'Alpha',
1583+
'emailAddress': 'alpha@example.com',
1584+
}
1585+
second = {
1586+
'accountId': '2222222222',
1587+
'accountName': 'Bravo',
1588+
'emailAddress': 'bravo@example.com',
1589+
}
1590+
third = {
1591+
'accountId': '3333333333',
1592+
'accountName': 'Delta',
1593+
'emailAddress': 'delta@example.com',
1594+
}
1595+
stub_account_sorting_flow(
1596+
accounts=[selected, second, third, first],
1597+
expected_sorted_accounts=[first, second, selected, third],
1598+
selected_account=selected,
1599+
)
1600+
assert sso_cmd(args, parsed_globals) == 0
1601+
1602+
def test_account_list_sorted_by_email(
1603+
self,
1604+
sso_cmd,
1605+
stub_account_sorting_flow,
1606+
args,
1607+
parsed_globals,
1608+
account_id,
1609+
):
1610+
selected = {
1611+
'accountId': account_id,
1612+
'emailAddress': 'charlie@example.com',
1613+
}
1614+
first = {
1615+
'accountId': '1111111111',
1616+
'emailAddress': 'alpha@example.com',
1617+
}
1618+
second = {
1619+
'accountId': '2222222222',
1620+
'emailAddress': 'bravo@example.com',
1621+
}
1622+
third = {
1623+
'accountId': '3333333333',
1624+
'emailAddress': 'delta@example.com',
1625+
}
1626+
stub_account_sorting_flow(
1627+
accounts=[selected, third, first, second],
1628+
expected_sorted_accounts=[first, second, selected, third],
1629+
selected_account=selected,
1630+
)
1631+
assert sso_cmd(args, parsed_globals) == 0
1632+
1633+
def test_account_list_sorted_by_account_id(
1634+
self,
1635+
sso_cmd,
1636+
stub_account_sorting_flow,
1637+
args,
1638+
parsed_globals,
1639+
account_id,
1640+
):
1641+
selected = {'accountId': account_id}
1642+
first = {'accountId': '1111111111'}
1643+
second = {'accountId': '2222222222'}
1644+
third = {'accountId': '3333333333'}
1645+
stub_account_sorting_flow(
1646+
accounts=[third, selected, first, second],
1647+
expected_sorted_accounts=[selected, first, second, third],
1648+
selected_account=selected,
1649+
)
1650+
assert sso_cmd(args, parsed_globals) == 0
1651+
1652+
def test_role_list_sorted_by_name(
1653+
self,
1654+
sso_cmd,
1655+
ptk_stubber,
1656+
stub_sso_list_accounts,
1657+
stub_sso_list_roles,
1658+
args,
1659+
parsed_globals,
1660+
start_url_prompt,
1661+
sso_region_prompt,
1662+
region_prompt,
1663+
output_prompt,
1664+
profile_prompt,
1665+
account_id,
1666+
):
1667+
selected_account = {
1668+
'accountId': account_id,
1669+
'emailAddress': 'account@example.com',
1670+
}
1671+
role_names_unsorted = [
1672+
'DataScientist',
1673+
'SystemAdministrator',
1674+
'AdministratorAccess',
1675+
]
1676+
expected_roles = [
1677+
'AdministratorAccess',
1678+
'DataScientist',
1679+
'SystemAdministrator',
1680+
]
1681+
account_select = None
1682+
role_select = SelectMenu(
1683+
answer='AdministratorAccess',
1684+
expected_choices=expected_roles,
1685+
)
1686+
role_profile_prompt = ProfilePrompt(
1687+
answer="dev",
1688+
expected_default=f"AdministratorAccess-{account_id}",
1689+
)
1690+
ptk_stubber.user_inputs = UserInputs(
1691+
session_prompt=RecommendedSessionPrompt(answer=""),
1692+
start_url_prompt=start_url_prompt,
1693+
sso_region_prompt=sso_region_prompt,
1694+
account_id_select=account_select,
1695+
role_name_select=role_select,
1696+
region_prompt=region_prompt,
1697+
output_prompt=output_prompt,
1698+
profile_prompt=role_profile_prompt,
1699+
)
1700+
stub_sso_list_accounts([selected_account])
1701+
stub_sso_list_roles(
1702+
role_names_unsorted,
1703+
expected_account_id=account_id,
1704+
)
1705+
assert sso_cmd(args, parsed_globals) == 0
1706+
15271707

15281708
class TestPrintConclusion:
15291709
def test_print_conclusion_default_profile_with_credentials(
@@ -2059,6 +2239,8 @@ def passes_validator(validator, text):
20592239
(ScopesValidator, "value-1, value-2 value3", None, False),
20602240
],
20612241
)
2242+
2243+
20622244
def test_validators(validator_cls, input_value, default, is_valid):
20632245
validator = validator_cls(default)
20642246
assert passes_validator(validator, input_value) == is_valid

0 commit comments

Comments
 (0)