Skip to content

Commit bacb577

Browse files
committed
Add Dashboard entity and Dashboards collection
1 parent b77f3f9 commit bacb577

2 files changed

Lines changed: 107 additions & 0 deletions

File tree

splunklib/client.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import socket
6565
from datetime import datetime, timedelta
6666
from time import sleep
67+
from typing import Any
6768
from urllib import parse
6869

6970
try:
@@ -102,6 +103,7 @@ def deprecated(message): # pyright: ignore[reportUnknownParameterType]
102103
PATH_APPS = "apps/local/"
103104
PATH_CAPABILITIES = "authorization/capabilities/"
104105
PATH_CONF = "configs/conf-%s/"
106+
PATH_DASHBOARDS = "data/ui/views/"
105107
PATH_PROPERTIES = "properties/"
106108
PATH_DEPLOYMENT_CLIENTS = "deployment/client/"
107109
PATH_DEPLOYMENT_TENANTS = "deployment/tenants/"
@@ -481,6 +483,15 @@ def capabilities(self):
481483
response = self.get(PATH_CAPABILITIES)
482484
return _load_atom(response, MATCH_ENTRY_CONTENT).capabilities
483485

486+
@property
487+
def dashboards(self):
488+
"""Returns the collection of dashboards for this Splunk instance.
489+
490+
:return: A :class:`Dashboards` collection of :class:`Dashboard`
491+
entities.
492+
"""
493+
return Dashboards(self)
494+
484495
@property
485496
def event_types(self):
486497
"""Returns the collection of event types defined in this Splunk instance.
@@ -3653,6 +3664,42 @@ def create(self, name, definition, **kwargs):
36533664
return Collection.create(self, name, definition=definition, **kwargs)
36543665

36553666

3667+
class Dashboard(Entity):
3668+
"""This class represents a dashboard (view) in Splunk."""
3669+
3670+
def __init__(self, service: "Service", path: str, **kwargs: Any) -> None:
3671+
Entity.__init__(self, service, path, **kwargs)
3672+
3673+
def export(self) -> str:
3674+
"""Returns the dashboard XML content.
3675+
3676+
:return: The dashboard XML definition.
3677+
:rtype: ``string``
3678+
"""
3679+
return self.content.get("eai:data", "") # pyright: ignore[reportUnknownVariableType]
3680+
3681+
3682+
class Dashboards(Collection):
3683+
"""This class represents a collection of dashboards. Retrieve this
3684+
collection using :meth:`Service.dashboards`."""
3685+
3686+
def __init__(self, service: "Service") -> None:
3687+
Collection.__init__(self, service, PATH_DASHBOARDS, item=Dashboard)
3688+
3689+
def create(self, name: str, xml: str, **kwargs: Any) -> Entity: # pyright: ignore[reportIncompatibleMethodOverride,reportImplicitOverride]
3690+
"""Creates a dashboard.
3691+
3692+
:param name: The name for the dashboard.
3693+
:type name: ``string``
3694+
:param xml: The dashboard XML definition.
3695+
:type xml: ``string``
3696+
:param kwargs: Additional arguments (optional).
3697+
:type kwargs: ``dict``
3698+
:return: The :class:`Dashboard` entity.
3699+
"""
3700+
return Collection.create(self, name, **{"eai:data": xml, **kwargs}) # pyright: ignore[reportUnknownVariableType]
3701+
3702+
36563703
class Settings(Entity):
36573704
"""This class represents configuration settings for a Splunk service.
36583705
Retrieve this collection using :meth:`Service.settings`."""

tests/unit/test_dashboard.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright © 2011-2026 Splunk, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"): you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
from unittest.mock import MagicMock, patch
16+
17+
import pytest
18+
19+
from splunklib.client import (
20+
PATH_DASHBOARDS,
21+
Collection,
22+
Dashboard,
23+
Dashboards,
24+
Entity,
25+
)
26+
27+
28+
class TestDashboard:
29+
def test_is_entity_subclass(self) -> None:
30+
assert issubclass(Dashboard, Entity)
31+
32+
def test_path_constant(self) -> None:
33+
assert PATH_DASHBOARDS == "data/ui/views/"
34+
35+
def test_export_returns_eai_data(self) -> None:
36+
dashboard = MagicMock(spec=Dashboard)
37+
dashboard.content = {"eai:data": "<dashboard><label>Test</label></dashboard>"}
38+
assert Dashboard.export(dashboard) == "<dashboard><label>Test</label></dashboard>"
39+
40+
def test_export_returns_empty_when_missing(self) -> None:
41+
dashboard = MagicMock(spec=Dashboard)
42+
dashboard.content = {}
43+
assert Dashboard.export(dashboard) == ""
44+
45+
46+
class TestDashboards:
47+
def test_is_collection_subclass(self) -> None:
48+
assert issubclass(Dashboards, Collection)
49+
50+
@patch.object(Collection, "create")
51+
def test_create_passes_xml_as_eai_data(self, mock_create: MagicMock) -> None:
52+
dashboards = Dashboards.__new__(Dashboards)
53+
xml = "<dashboard><label>Test</label></dashboard>"
54+
Dashboards.create(dashboards, "test_dash", xml)
55+
mock_create.assert_called_once_with(dashboards, "test_dash", **{"eai:data": xml})
56+
57+
def test_create_raises_on_missing_xml(self) -> None:
58+
dashboards = Dashboards.__new__(Dashboards)
59+
with pytest.raises(TypeError):
60+
Dashboards.create(dashboards, "test_dash") # pyright: ignore[reportCallIssue]

0 commit comments

Comments
 (0)