Skip to content

Commit 212c5fb

Browse files
author
GitHub Actions
committed
Auto commit from main repo: manual-sync
1 parent b805b66 commit 212c5fb

4 files changed

Lines changed: 90 additions & 1 deletion

File tree

armis_sdk/clients/assets_client.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from armis_sdk.core.base_entity_client import BaseEntityClient
1414
from armis_sdk.entities.asset import Asset
1515
from armis_sdk.entities.asset import AssetT
16+
from armis_sdk.entities.asset_field_description import AssetFieldDescription
1617
from armis_sdk.entities.device import Device
1718
from armis_sdk.types.asset_id_source import AssetIdSource
1819

@@ -131,6 +132,42 @@ async def main():
131132
async for item in self._list_assets(asset_class, fields, filter_):
132133
yield item
133134

135+
async def list_fields(
136+
self, asset_class: Type[AssetT]
137+
) -> AsyncIterator[AssetFieldDescription]:
138+
"""List all available fields for a given asset class.
139+
140+
Args:
141+
asset_class: The asset class to list fields for. Must inherit from [Asset][armis_sdk.entities.asset.Asset].
142+
143+
Yields:
144+
Field descriptions including field name, type, and other metadata.
145+
146+
Example:
147+
```python linenums="1" hl_lines="9"
148+
import asyncio
149+
150+
from armis_sdk.clients.assets_client import AssetsClient
151+
from armis_sdk.entities.device import Device
152+
153+
async def main():
154+
assets_client = AssetsClient()
155+
156+
async for field in assets_client.list_fields(Device):
157+
print(f"{field.name}: {field.type}")
158+
159+
asyncio.run(main())
160+
```
161+
"""
162+
async with self._armis_client.client() as client:
163+
response = await client.get(
164+
"/v3/assets/_search/fields",
165+
params={"asset_type": asset_class.asset_type},
166+
)
167+
data = response_utils.get_data_dict(response)
168+
for item in data["items"]:
169+
yield AssetFieldDescription.model_validate(item)
170+
134171
async def update(
135172
self,
136173
assets: list[AssetT],
@@ -281,6 +318,10 @@ def _get_device_asset_id(
281318
def _is_custom_field(cls, field: str) -> bool:
282319
return field.startswith("custom.")
283320

321+
@classmethod
322+
def _is_integration_field(cls, field: str) -> bool:
323+
return field.startswith("integration.")
324+
284325
async def _list_assets(
285326
self,
286327
asset_class: Type[AssetT],
@@ -322,6 +363,9 @@ def _validate_fields(
322363
if cls._is_custom_field(field):
323364
continue
324365

366+
if cls._is_integration_field(field):
367+
continue
368+
325369
if allow_model_members and field in all_fields:
326370
continue
327371

armis_sdk/entities/asset.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class Asset(BaseEntity):
2222
custom: dict[str, Any] = Field(default_factory=dict)
2323
"""Custom properties of the asset. Values can by anything."""
2424

25+
integration: dict[str, Any] = Field(default_factory=dict)
26+
"""Integration properties of the asset. Values can by anything."""
27+
2528
@classmethod
2629
def from_search_result(cls: Type[AssetT], data: dict) -> AssetT:
2730
fields: DefaultDict[str, Any] = collections.defaultdict(dict)
@@ -38,4 +41,7 @@ def from_search_result(cls: Type[AssetT], data: dict) -> AssetT:
3841
def all_fields(cls) -> set[str]:
3942
# Pylint doesn't recognize that "cls.model_fields" is a dict and not a method
4043
# so it's complaining that the method doesn't have a "keys" attribute.
41-
return set(cls.model_fields.keys()) - {"custom"} # pylint: disable=no-member
44+
return set(cls.model_fields.keys()) - {
45+
"custom",
46+
"integration",
47+
} # pylint: disable=no-member
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from armis_sdk.core.base_entity import BaseEntity
2+
3+
4+
class AssetFieldDescription(BaseEntity):
5+
name: str
6+
type: str
7+
is_list: bool = False

tests/armis_sdk/clients/assets_client_test.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from armis_sdk.core.armis_error import ArmisError
88
from armis_sdk.core.armis_error import BulkUpdateError
99
from armis_sdk.entities.asset import Asset
10+
from armis_sdk.entities.asset_field_description import AssetFieldDescription
1011
from armis_sdk.entities.device import Device
1112
from tests.armis_sdk.clients import assets_test_data
1213

@@ -403,3 +404,34 @@ async def test_update_with_validation_errors(assets, fields, expected_error):
403404

404405
with pytest.raises(ArmisError, match=expected_error):
405406
await assets_client.update(assets, fields)
407+
408+
409+
async def test_list_fields(httpx_mock: pytest_httpx.HTTPXMock):
410+
httpx_mock.add_response(
411+
url="https://api.armis.com/v3/assets/_search/fields?asset_type=DEVICE",
412+
method="GET",
413+
json={
414+
"items": [
415+
{"name": "device_id", "type": "integer", "is_list": False},
416+
{"name": "names", "type": "string", "is_list": True},
417+
{"name": "custom.Size", "type": "enum", "is_list": False},
418+
{
419+
"name": "integration.qualys_agent_id",
420+
"type": "string",
421+
"is_list": False,
422+
},
423+
]
424+
},
425+
)
426+
427+
assets_client = AssetsClient()
428+
fields = [field async for field in assets_client.list_fields(Device)]
429+
430+
assert fields == [
431+
AssetFieldDescription(name="device_id", type="integer", is_list=False),
432+
AssetFieldDescription(name="names", type="string", is_list=True),
433+
AssetFieldDescription(name="custom.Size", type="enum", is_list=False),
434+
AssetFieldDescription(
435+
name="integration.qualys_agent_id", type="string", is_list=False
436+
),
437+
]

0 commit comments

Comments
 (0)