Skip to content

Commit 5b1e3ef

Browse files
authored
Set xuid as unique_id and gamertag as title in Xbox config flow (home-assistant#154693)
1 parent d607323 commit 5b1e3ef

File tree

4 files changed

+132
-10
lines changed

4 files changed

+132
-10
lines changed

homeassistant/components/xbox/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,36 @@ async def async_setup_entry(hass: HomeAssistant, entry: XboxConfigEntry) -> bool
5454

5555
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
5656

57+
await async_migrate_unique_id(hass, entry)
5758
return True
5859

5960

6061
async def async_unload_entry(hass: HomeAssistant, entry: XboxConfigEntry) -> bool:
6162
"""Unload a config entry."""
6263

6364
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
65+
66+
67+
async def async_migrate_unique_id(hass: HomeAssistant, entry: XboxConfigEntry) -> bool:
68+
"""Migrate config entry.
69+
70+
Migration requires runtime data
71+
"""
72+
73+
if entry.version == 1 and entry.minor_version < 2:
74+
# Migrate unique_id from `xbox` to account xuid and
75+
# change generic entry name to user's gamertag
76+
return hass.config_entries.async_update_entry(
77+
entry,
78+
unique_id=entry.runtime_data.client.xuid,
79+
title=(
80+
entry.runtime_data.data.presence[
81+
entry.runtime_data.client.xuid
82+
].gamertag
83+
if entry.title == "Home Assistant Cloud"
84+
else entry.title
85+
),
86+
minor_version=2,
87+
)
88+
89+
return True

homeassistant/components/xbox/config_flow.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
import logging
44
from typing import Any
55

6+
from xbox.webapi.api.client import XboxLiveClient
7+
from xbox.webapi.authentication.manager import AuthenticationManager
8+
from xbox.webapi.authentication.models import OAuth2TokenResponse
9+
from xbox.webapi.common.signed_session import SignedSession
10+
611
from homeassistant.config_entries import ConfigFlowResult
712
from homeassistant.helpers import config_entry_oauth2_flow
813

@@ -16,6 +21,8 @@ class OAuth2FlowHandler(
1621

1722
DOMAIN = DOMAIN
1823

24+
MINOR_VERSION = 2
25+
1926
@property
2027
def logger(self) -> logging.Logger:
2128
"""Return logger."""
@@ -31,9 +38,23 @@ async def async_step_user(
3138
self, user_input: dict[str, Any] | None = None
3239
) -> ConfigFlowResult:
3340
"""Handle a flow start."""
34-
await self.async_set_unique_id(DOMAIN)
3541

3642
if self._async_current_entries():
3743
return self.async_abort(reason="single_instance_allowed")
3844

3945
return await super().async_step_user(user_input)
46+
47+
async def async_oauth_create_entry(self, data: dict) -> ConfigFlowResult:
48+
"""Create an entry for the flow."""
49+
50+
async with SignedSession() as session:
51+
auth = AuthenticationManager(session, "", "", "")
52+
auth.oauth = OAuth2TokenResponse(**data["token"])
53+
await auth.refresh_tokens()
54+
55+
client = XboxLiveClient(auth)
56+
57+
me = await client.people.get_friends_own_batch([client.xuid])
58+
59+
await self.async_set_unique_id(client.xuid)
60+
return self.async_create_entry(title=me.people[0].gamertag, data=data)

tests/components/xbox/conftest.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,36 @@ def mock_config_entry() -> MockConfigEntry:
6565
"user_id": "AAAAAAAAAAAAAAAAAAAAA",
6666
},
6767
},
68-
unique_id="xbox",
6968
)
7069

7170

71+
@pytest.fixture(name="authentication_manager")
72+
def mock_authentication_manager() -> Generator[AsyncMock]:
73+
"""Mock xbox-webapi AuthenticationManager."""
74+
75+
with (
76+
patch(
77+
"homeassistant.components.xbox.config_flow.AuthenticationManager",
78+
autospec=True,
79+
) as mock_client,
80+
):
81+
client = mock_client.return_value
82+
83+
yield client
84+
85+
7286
@pytest.fixture(name="signed_session")
7387
def mock_signed_session() -> Generator[AsyncMock]:
7488
"""Mock xbox-webapi SignedSession."""
7589

76-
with patch(
77-
"homeassistant.components.xbox.SignedSession", autospec=True
78-
) as mock_client:
90+
with (
91+
patch(
92+
"homeassistant.components.xbox.SignedSession", autospec=True
93+
) as mock_client,
94+
patch(
95+
"homeassistant.components.xbox.config_flow.SignedSession", new=mock_client
96+
),
97+
):
7998
client = mock_client.return_value
8099

81100
yield client
@@ -85,9 +104,14 @@ def mock_signed_session() -> Generator[AsyncMock]:
85104
def mock_xbox_live_client(signed_session) -> Generator[AsyncMock]:
86105
"""Mock xbox-webapi XboxLiveClient."""
87106

88-
with patch(
89-
"homeassistant.components.xbox.XboxLiveClient", autospec=True
90-
) as mock_client:
107+
with (
108+
patch(
109+
"homeassistant.components.xbox.XboxLiveClient", autospec=True
110+
) as mock_client,
111+
patch(
112+
"homeassistant.components.xbox.config_flow.XboxLiveClient", new=mock_client
113+
),
114+
):
91115
client = mock_client.return_value
92116

93117
client.smartglass = AsyncMock()
@@ -110,4 +134,7 @@ def mock_xbox_live_client(signed_session) -> Generator[AsyncMock]:
110134
client.people.get_friends_own.return_value = PeopleResponse(
111135
**load_json_object_fixture("people_friends_own.json", DOMAIN)
112136
)
137+
138+
client.xuid = "271958441785640"
139+
113140
yield client

tests/components/xbox/test_config_flow.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ async def test_abort_if_existing_entry(hass: HomeAssistant) -> None:
2929
assert result["reason"] == "single_instance_allowed"
3030

3131

32-
@pytest.mark.usefixtures("current_request_with_host")
32+
@pytest.mark.usefixtures(
33+
"current_request_with_host",
34+
"xbox_live_client",
35+
"authentication_manager",
36+
)
3337
async def test_full_flow(
3438
hass: HomeAssistant,
3539
hass_client_no_auth: ClientSessionGenerator,
@@ -68,13 +72,57 @@ async def test_full_flow(
6872
"access_token": "mock-access-token",
6973
"type": "Bearer",
7074
"expires_in": 60,
75+
"scope": "XboxLive.signin XboxLive.offline_access",
76+
"service": "xbox",
77+
"token_type": "bearer",
78+
"user_id": "AAAAAAAAAAAAAAAAAAAAA",
7179
},
7280
)
7381

7482
with patch(
7583
"homeassistant.components.xbox.async_setup_entry", return_value=True
7684
) as mock_setup:
77-
await hass.config_entries.flow.async_configure(result["flow_id"])
85+
result = await hass.config_entries.flow.async_configure(result["flow_id"])
7886

87+
assert result["result"].unique_id == "271958441785640"
88+
assert result["result"].title == "GSR Ae"
7989
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
8090
assert len(mock_setup.mock_calls) == 1
91+
92+
93+
@pytest.mark.usefixtures("xbox_live_client")
94+
async def test_unique_id_migration(
95+
hass: HomeAssistant,
96+
) -> None:
97+
"""Test config entry unique_id migration."""
98+
config_entry = MockConfigEntry(
99+
domain=DOMAIN,
100+
title="Home Assistant Cloud",
101+
data={
102+
"auth_implementation": "cloud",
103+
"token": {
104+
"access_token": "1234567890",
105+
"expires_at": 1760697327.7298331,
106+
"expires_in": 3600,
107+
"refresh_token": "0987654321",
108+
"scope": "XboxLive.signin XboxLive.offline_access",
109+
"service": "xbox",
110+
"token_type": "bearer",
111+
"user_id": "AAAAAAAAAAAAAAAAAAAAA",
112+
},
113+
},
114+
unique_id=DOMAIN,
115+
version=1,
116+
minor_version=1,
117+
)
118+
119+
config_entry.add_to_hass(hass)
120+
121+
await hass.config_entries.async_setup(config_entry.entry_id)
122+
await hass.async_block_till_done()
123+
124+
assert config_entry.state is config_entries.ConfigEntryState.LOADED
125+
assert config_entry.version == 1
126+
assert config_entry.minor_version == 2
127+
assert config_entry.unique_id == "271958441785640"
128+
assert config_entry.title == "GSR Ae"

0 commit comments

Comments
 (0)