Bump bring-api to 1.0.0 (#136657)

This commit is contained in:
Manu 2025-01-28 13:24:44 +01:00 committed by GitHub
parent 7f3e56eb58
commit fa4b93da2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 311 additions and 271 deletions

View File

@ -63,7 +63,8 @@ class BringConfigFlow(ConfigFlow, domain=DOMAIN):
):
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=self.info.get("name") or user_input[CONF_EMAIL], data=user_input
title=self.info.name or user_input[CONF_EMAIL],
data=user_input,
)
return self.async_show_form(

View File

@ -2,6 +2,7 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta
import logging
@ -12,6 +13,7 @@ from bring_api import (
BringRequestException,
)
from bring_api.types import BringItemsResponse, BringList, BringUserSettingsResponse
from mashumaro.mixins.orjson import DataClassORJSONMixin
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_EMAIL
@ -24,9 +26,13 @@ from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
class BringData(BringList, BringItemsResponse):
@dataclass(frozen=True)
class BringData(DataClassORJSONMixin):
"""Coordinator data class."""
lst: BringList
content: BringItemsResponse
class BringDataUpdateCoordinator(DataUpdateCoordinator[dict[str, BringData]]):
"""A Bring Data Update Coordinator."""
@ -67,11 +73,11 @@ class BringDataUpdateCoordinator(DataUpdateCoordinator[dict[str, BringData]]):
return self.data
list_dict: dict[str, BringData] = {}
for lst in lists_response["lists"]:
if (ctx := set(self.async_contexts())) and lst["listUuid"] not in ctx:
for lst in lists_response.lists:
if (ctx := set(self.async_contexts())) and lst.listUuid not in ctx:
continue
try:
items = await self.bring.get_list(lst["listUuid"])
items = await self.bring.get_list(lst.listUuid)
except BringRequestException as e:
raise UpdateFailed(
"Unable to connect and retrieve data from bring"
@ -79,7 +85,7 @@ class BringDataUpdateCoordinator(DataUpdateCoordinator[dict[str, BringData]]):
except BringParseException as e:
raise UpdateFailed("Unable to parse response from bring") from e
else:
list_dict[lst["listUuid"]] = BringData(**lst, **items)
list_dict[lst.listUuid] = BringData(lst, items)
return list_dict

View File

@ -2,15 +2,16 @@
from __future__ import annotations
from typing import Any
from homeassistant.core import HomeAssistant
from . import BringConfigEntry
from .coordinator import BringData
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: BringConfigEntry
) -> dict[str, BringData]:
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
return config_entry.runtime_data.data
return {k: v.to_dict() for k, v in config_entry.runtime_data.data.items()}

View File

@ -20,13 +20,13 @@ class BringBaseEntity(CoordinatorEntity[BringDataUpdateCoordinator]):
bring_list: BringData,
) -> None:
"""Initialize the entity."""
super().__init__(coordinator, bring_list["listUuid"])
super().__init__(coordinator, bring_list.lst.listUuid)
self._list_uuid = bring_list["listUuid"]
self._list_uuid = bring_list.lst.listUuid
self.device_info = DeviceInfo(
entry_type=DeviceEntryType.SERVICE,
name=bring_list["name"],
name=bring_list.lst.name,
identifiers={
(DOMAIN, f"{coordinator.config_entry.unique_id}_{self._list_uuid}")
},

View File

@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["bring_api"],
"requirements": ["bring-api==0.9.1"]
"requirements": ["bring-api==1.0.0"]
}

View File

@ -65,7 +65,7 @@ SENSOR_DESCRIPTIONS: tuple[BringSensorEntityDescription, ...] = (
translation_key=BringSensor.LIST_LANGUAGE,
value_fn=(
lambda lst, settings: x.lower()
if (x := list_language(lst["listUuid"], settings))
if (x := list_language(lst.lst.listUuid, settings))
else None
),
entity_category=EntityCategory.DIAGNOSTIC,
@ -75,7 +75,7 @@ SENSOR_DESCRIPTIONS: tuple[BringSensorEntityDescription, ...] = (
BringSensorEntityDescription(
key=BringSensor.LIST_ACCESS,
translation_key=BringSensor.LIST_ACCESS,
value_fn=lambda lst, _: lst["status"].lower(),
value_fn=lambda lst, _: lst.content.status.value.lower(),
entity_category=EntityCategory.DIAGNOSTIC,
options=["registered", "shared", "invitation"],
device_class=SensorDeviceClass.ENUM,

View File

@ -2,6 +2,7 @@
from __future__ import annotations
from itertools import chain
from typing import TYPE_CHECKING
import uuid
@ -59,7 +60,7 @@ async def async_setup_entry(
SERVICE_PUSH_NOTIFICATION,
{
vol.Required(ATTR_NOTIFICATION_TYPE): vol.All(
vol.Upper, cv.enum(BringNotificationType)
vol.Upper, vol.Coerce(BringNotificationType)
),
vol.Optional(ATTR_ITEM_NAME): cv.string,
},
@ -92,21 +93,21 @@ class BringTodoListEntity(BringBaseEntity, TodoListEntity):
return [
*(
TodoItem(
uid=item["uuid"],
summary=item["itemId"],
description=item["specification"] or "",
uid=item.uuid,
summary=item.itemId,
description=item.specification,
status=TodoItemStatus.NEEDS_ACTION,
)
for item in self.bring_list["purchase"]
for item in self.bring_list.content.items.purchase
),
*(
TodoItem(
uid=item["uuid"],
summary=item["itemId"],
description=item["specification"] or "",
uid=item.uuid,
summary=item.itemId,
description=item.specification,
status=TodoItemStatus.COMPLETED,
)
for item in self.bring_list["recently"]
for item in self.bring_list.content.items.recently
),
]
@ -119,7 +120,7 @@ class BringTodoListEntity(BringBaseEntity, TodoListEntity):
"""Add an item to the To-do list."""
try:
await self.coordinator.bring.save_item(
self.bring_list["listUuid"],
self._list_uuid,
item.summary or "",
item.description or "",
str(uuid.uuid4()),
@ -154,26 +155,25 @@ class BringTodoListEntity(BringBaseEntity, TodoListEntity):
bring_list = self.bring_list
bring_purchase_item = next(
(i for i in bring_list["purchase"] if i["uuid"] == item.uid),
current_item = next(
(
i
for i in chain(
bring_list.content.items.purchase, bring_list.content.items.recently
)
if i.uuid == item.uid
),
None,
)
bring_recently_item = next(
(i for i in bring_list["recently"] if i["uuid"] == item.uid),
None,
)
current_item = bring_purchase_item or bring_recently_item
if TYPE_CHECKING:
assert item.uid
assert current_item
if item.summary == current_item["itemId"]:
if item.summary == current_item.itemId:
try:
await self.coordinator.bring.batch_update_list(
bring_list["listUuid"],
self._list_uuid,
BringItem(
itemId=item.summary or "",
spec=item.description or "",
@ -192,10 +192,10 @@ class BringTodoListEntity(BringBaseEntity, TodoListEntity):
else:
try:
await self.coordinator.bring.batch_update_list(
bring_list["listUuid"],
self._list_uuid,
[
BringItem(
itemId=current_item["itemId"],
itemId=current_item.itemId,
spec=item.description or "",
uuid=item.uid,
operation=BringItemOperation.REMOVE,
@ -225,7 +225,7 @@ class BringTodoListEntity(BringBaseEntity, TodoListEntity):
try:
await self.coordinator.bring.batch_update_list(
self.bring_list["listUuid"],
self._list_uuid,
[
BringItem(
itemId=uid,

View File

@ -14,27 +14,25 @@ def list_language(
"""Get the lists language setting."""
try:
list_settings = next(
filter(
lambda x: x["listUuid"] == list_uuid,
user_settings["userlistsettings"],
)
filter(lambda x: x.listUuid == list_uuid, user_settings.userlistsettings)
)
return next(
filter(
lambda x: x["key"] == "listArticleLanguage",
list_settings["usersettings"],
return (
next(
filter(
lambda x: x.key == "listArticleLanguage", list_settings.usersettings
)
)
)["value"]
).value
except (StopIteration, KeyError):
except StopIteration:
return None
def sum_attributes(bring_list: BringData, attribute: str) -> int:
"""Count items with given attribute set."""
return sum(
item["attributes"][0]["content"][attribute]
for item in bring_list["purchase"]
if len(item.get("attributes", []))
getattr(item.attributes[0].content, attribute)
for item in bring_list.content.items.purchase
if item.attributes
)

2
requirements_all.txt generated
View File

@ -644,7 +644,7 @@ boto3==1.34.131
botocore==1.34.131
# homeassistant.components.bring
bring-api==0.9.1
bring-api==1.0.0
# homeassistant.components.broadlink
broadlink==0.19.0

View File

@ -564,7 +564,7 @@ boschshcpy==0.2.91
botocore==1.34.131
# homeassistant.components.bring
bring-api==0.9.1
bring-api==1.0.0
# homeassistant.components.broadlink
broadlink==0.19.0

View File

@ -1,17 +1,21 @@
"""Common fixtures for the Bring! tests."""
from collections.abc import Generator
from typing import cast
from unittest.mock import AsyncMock, patch
import uuid
from bring_api.types import BringAuthResponse
from bring_api.types import (
BringAuthResponse,
BringItemsResponse,
BringListResponse,
BringUserSettingsResponse,
)
import pytest
from homeassistant.components.bring.const import DOMAIN
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from tests.common import MockConfigEntry, load_json_object_fixture
from tests.common import MockConfigEntry, load_fixture
EMAIL = "test-email"
PASSWORD = "test-password"
@ -44,11 +48,17 @@ def mock_bring_client() -> Generator[AsyncMock]:
client = mock_client.return_value
client.uuid = UUID
client.mail = EMAIL
client.login.return_value = cast(BringAuthResponse, {"name": "Bring"})
client.load_lists.return_value = load_json_object_fixture("lists.json", DOMAIN)
client.get_list.return_value = load_json_object_fixture("items.json", DOMAIN)
client.get_all_user_settings.return_value = load_json_object_fixture(
"usersettings.json", DOMAIN
client.login.return_value = BringAuthResponse.from_json(
load_fixture("login.json", DOMAIN)
)
client.load_lists.return_value = BringListResponse.from_json(
load_fixture("lists.json", DOMAIN)
)
client.get_list.return_value = BringItemsResponse.from_json(
load_fixture("items.json", DOMAIN)
)
client.get_all_user_settings.return_value = BringUserSettingsResponse.from_json(
load_fixture("usersettings.json", DOMAIN)
)
yield client

View File

@ -1,44 +1,46 @@
{
"uuid": "77a151f8-77c4-47a3-8295-c750a0e69d4f",
"status": "REGISTERED",
"purchase": [
{
"uuid": "b5d0790b-5f32-4d5c-91da-e29066f167de",
"itemId": "Paprika",
"specification": "Rot",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
"items": {
"purchase": [
{
"uuid": "b5d0790b-5f32-4d5c-91da-e29066f167de",
"itemId": "Paprika",
"specification": "Rot",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
}
}
}
]
},
{
"uuid": "72d370ab-d8ca-4e41-b956-91df94795b4e",
"itemId": "Pouletbrüstli",
"specification": "Bio",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
]
},
{
"uuid": "72d370ab-d8ca-4e41-b956-91df94795b4e",
"itemId": "Pouletbrüstli",
"specification": "Bio",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
}
}
}
]
}
],
"recently": [
{
"uuid": "fc8db30a-647e-4e6c-9d71-3b85d6a2d954",
"itemId": "Ananas",
"specification": "",
"attributes": []
}
]
]
}
],
"recently": [
{
"uuid": "fc8db30a-647e-4e6c-9d71-3b85d6a2d954",
"itemId": "Ananas",
"specification": "",
"attributes": []
}
]
}
}

View File

@ -1,44 +1,46 @@
{
"uuid": "77a151f8-77c4-47a3-8295-c750a0e69d4f",
"status": "INVITATION",
"purchase": [
{
"uuid": "b5d0790b-5f32-4d5c-91da-e29066f167de",
"itemId": "Paprika",
"specification": "Rot",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
"items": {
"purchase": [
{
"uuid": "b5d0790b-5f32-4d5c-91da-e29066f167de",
"itemId": "Paprika",
"specification": "Rot",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
}
}
}
]
},
{
"uuid": "72d370ab-d8ca-4e41-b956-91df94795b4e",
"itemId": "Pouletbrüstli",
"specification": "Bio",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
]
},
{
"uuid": "72d370ab-d8ca-4e41-b956-91df94795b4e",
"itemId": "Pouletbrüstli",
"specification": "Bio",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
}
}
}
]
}
],
"recently": [
{
"uuid": "fc8db30a-647e-4e6c-9d71-3b85d6a2d954",
"itemId": "Ananas",
"specification": "",
"attributes": []
}
]
]
}
],
"recently": [
{
"uuid": "fc8db30a-647e-4e6c-9d71-3b85d6a2d954",
"itemId": "Ananas",
"specification": "",
"attributes": []
}
]
}
}

View File

@ -1,44 +1,46 @@
{
"uuid": "77a151f8-77c4-47a3-8295-c750a0e69d4f",
"status": "SHARED",
"purchase": [
{
"uuid": "b5d0790b-5f32-4d5c-91da-e29066f167de",
"itemId": "Paprika",
"specification": "Rot",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
"items": {
"purchase": [
{
"uuid": "b5d0790b-5f32-4d5c-91da-e29066f167de",
"itemId": "Paprika",
"specification": "Rot",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
}
}
}
]
},
{
"uuid": "72d370ab-d8ca-4e41-b956-91df94795b4e",
"itemId": "Pouletbrüstli",
"specification": "Bio",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
]
},
{
"uuid": "72d370ab-d8ca-4e41-b956-91df94795b4e",
"itemId": "Pouletbrüstli",
"specification": "Bio",
"attributes": [
{
"type": "PURCHASE_CONDITIONS",
"content": {
"urgent": true,
"convenient": true,
"discounted": true
}
}
}
]
}
],
"recently": [
{
"uuid": "fc8db30a-647e-4e6c-9d71-3b85d6a2d954",
"itemId": "Ananas",
"specification": "",
"attributes": []
}
]
]
}
],
"recently": [
{
"uuid": "fc8db30a-647e-4e6c-9d71-3b85d6a2d954",
"itemId": "Ananas",
"specification": "",
"attributes": []
}
]
}
}

View File

@ -0,0 +1,12 @@
{
"uuid": "4d717571-174a-4bc1-ab24-929c7227ca43",
"publicUuid": "9a21fdfc-63a4-441a-afc1-ef3030605a9d",
"email": "test-email",
"name": "Bring",
"photoPath": "",
"bringListUUID": "e542eef6-dba7-4c31-a52c-29e6ab9d83a5",
"access_token": "ACCESS_TOKEN",
"refresh_token": "REFRESH_TOKEN",
"token_type": "Bearer",
"expires_in": 604799
}

View File

@ -2,100 +2,112 @@
# name: test_diagnostics
dict({
'b4776778-7f6c-496e-951b-92a35d3db0dd': dict({
'listUuid': 'b4776778-7f6c-496e-951b-92a35d3db0dd',
'name': 'Baumarkt',
'purchase': list([
dict({
'attributes': list([
'content': dict({
'items': dict({
'purchase': list([
dict({
'content': dict({
'convenient': True,
'discounted': True,
'urgent': True,
}),
'type': 'PURCHASE_CONDITIONS',
'attributes': list([
dict({
'content': dict({
'convenient': True,
'discounted': True,
'urgent': True,
}),
'type': 'PURCHASE_CONDITIONS',
}),
]),
'itemId': 'Paprika',
'specification': 'Rot',
'uuid': 'b5d0790b-5f32-4d5c-91da-e29066f167de',
}),
dict({
'attributes': list([
dict({
'content': dict({
'convenient': True,
'discounted': True,
'urgent': True,
}),
'type': 'PURCHASE_CONDITIONS',
}),
]),
'itemId': 'Pouletbrüstli',
'specification': 'Bio',
'uuid': '72d370ab-d8ca-4e41-b956-91df94795b4e',
}),
]),
'itemId': 'Paprika',
'specification': 'Rot',
'uuid': 'b5d0790b-5f32-4d5c-91da-e29066f167de',
}),
dict({
'attributes': list([
'recently': list([
dict({
'content': dict({
'convenient': True,
'discounted': True,
'urgent': True,
}),
'type': 'PURCHASE_CONDITIONS',
'attributes': list([
]),
'itemId': 'Ananas',
'specification': '',
'uuid': 'fc8db30a-647e-4e6c-9d71-3b85d6a2d954',
}),
]),
'itemId': 'Pouletbrüstli',
'specification': 'Bio',
'uuid': '72d370ab-d8ca-4e41-b956-91df94795b4e',
}),
]),
'recently': list([
dict({
'attributes': list([
]),
'itemId': 'Ananas',
'specification': '',
'uuid': 'fc8db30a-647e-4e6c-9d71-3b85d6a2d954',
}),
]),
'status': 'REGISTERED',
'theme': 'ch.publisheria.bring.theme.home',
'uuid': '77a151f8-77c4-47a3-8295-c750a0e69d4f',
'status': 'REGISTERED',
'uuid': '77a151f8-77c4-47a3-8295-c750a0e69d4f',
}),
'lst': dict({
'listUuid': 'b4776778-7f6c-496e-951b-92a35d3db0dd',
'name': 'Baumarkt',
'theme': 'ch.publisheria.bring.theme.home',
}),
}),
'e542eef6-dba7-4c31-a52c-29e6ab9d83a5': dict({
'listUuid': 'e542eef6-dba7-4c31-a52c-29e6ab9d83a5',
'name': 'Einkauf',
'purchase': list([
dict({
'attributes': list([
'content': dict({
'items': dict({
'purchase': list([
dict({
'content': dict({
'convenient': True,
'discounted': True,
'urgent': True,
}),
'type': 'PURCHASE_CONDITIONS',
'attributes': list([
dict({
'content': dict({
'convenient': True,
'discounted': True,
'urgent': True,
}),
'type': 'PURCHASE_CONDITIONS',
}),
]),
'itemId': 'Paprika',
'specification': 'Rot',
'uuid': 'b5d0790b-5f32-4d5c-91da-e29066f167de',
}),
dict({
'attributes': list([
dict({
'content': dict({
'convenient': True,
'discounted': True,
'urgent': True,
}),
'type': 'PURCHASE_CONDITIONS',
}),
]),
'itemId': 'Pouletbrüstli',
'specification': 'Bio',
'uuid': '72d370ab-d8ca-4e41-b956-91df94795b4e',
}),
]),
'itemId': 'Paprika',
'specification': 'Rot',
'uuid': 'b5d0790b-5f32-4d5c-91da-e29066f167de',
}),
dict({
'attributes': list([
'recently': list([
dict({
'content': dict({
'convenient': True,
'discounted': True,
'urgent': True,
}),
'type': 'PURCHASE_CONDITIONS',
'attributes': list([
]),
'itemId': 'Ananas',
'specification': '',
'uuid': 'fc8db30a-647e-4e6c-9d71-3b85d6a2d954',
}),
]),
'itemId': 'Pouletbrüstli',
'specification': 'Bio',
'uuid': '72d370ab-d8ca-4e41-b956-91df94795b4e',
}),
]),
'recently': list([
dict({
'attributes': list([
]),
'itemId': 'Ananas',
'specification': '',
'uuid': 'fc8db30a-647e-4e6c-9d71-3b85d6a2d954',
}),
]),
'status': 'REGISTERED',
'theme': 'ch.publisheria.bring.theme.home',
'uuid': '77a151f8-77c4-47a3-8295-c750a0e69d4f',
'status': 'REGISTERED',
'uuid': '77a151f8-77c4-47a3-8295-c750a0e69d4f',
}),
'lst': dict({
'listUuid': 'e542eef6-dba7-4c31-a52c-29e6ab9d83a5',
'name': 'Einkauf',
'theme': 'ch.publisheria.bring.theme.home',
}),
}),
})
# ---

View File

@ -3,6 +3,7 @@
from collections.abc import Generator
from unittest.mock import AsyncMock, patch
from bring_api import BringItemsResponse
import pytest
from syrupy.assertion import SnapshotAssertion
@ -12,7 +13,7 @@ from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, load_json_object_fixture, snapshot_platform
from tests.common import MockConfigEntry, load_fixture, snapshot_platform
@pytest.fixture(autouse=True)
@ -62,10 +63,9 @@ async def test_list_access_states(
) -> None:
"""Snapshot test states of list access sensor."""
mock_bring_client.get_list.return_value = load_json_object_fixture(
f"{fixture}.json", DOMAIN
mock_bring_client.get_list.return_value = BringItemsResponse.from_json(
load_fixture(f"{fixture}.json", DOMAIN)
)
bring_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(bring_config_entry.entry_id)
await hass.async_block_till_done()

View File

@ -1,15 +1,13 @@
"""Test for utility functions of the Bring! integration."""
from typing import cast
from bring_api import BringUserSettingsResponse
from bring_api import BringItemsResponse, BringListResponse, BringUserSettingsResponse
import pytest
from homeassistant.components.bring.const import DOMAIN
from homeassistant.components.bring.coordinator import BringData
from homeassistant.components.bring.util import list_language, sum_attributes
from tests.common import load_json_object_fixture
from tests.common import load_fixture
@pytest.mark.parametrize(
@ -17,7 +15,7 @@ from tests.common import load_json_object_fixture
[
("e542eef6-dba7-4c31-a52c-29e6ab9d83a5", "de-DE"),
("b4776778-7f6c-496e-951b-92a35d3db0dd", "en-US"),
("00000000-0000-0000-0000-00000000", None),
("00000000-0000-0000-0000-000000000000", None),
],
)
def test_list_language(list_uuid: str, expected: str | None) -> None:
@ -25,10 +23,7 @@ def test_list_language(list_uuid: str, expected: str | None) -> None:
result = list_language(
list_uuid,
cast(
BringUserSettingsResponse,
load_json_object_fixture("usersettings.json", DOMAIN),
),
BringUserSettingsResponse.from_json(load_fixture("usersettings.json", DOMAIN)),
)
assert result == expected
@ -44,12 +39,11 @@ def test_list_language(list_uuid: str, expected: str | None) -> None:
)
def test_sum_attributes(attribute: str, expected: int) -> None:
"""Test function sum_attributes."""
items = BringItemsResponse.from_json(load_fixture("items.json", DOMAIN))
lst = BringListResponse.from_json(load_fixture("lists.json", DOMAIN))
result = sum_attributes(
cast(
BringData,
load_json_object_fixture("items.json", DOMAIN),
),
BringData(lst.lists[0], items),
attribute,
)