Skip to content

Commit 83a3135

Browse files
author
Sam Stelle
committed
Added URI handling in collections to escape slashes.
1 parent bc3206e commit 83a3135

File tree

2 files changed

+67
-7
lines changed

2 files changed

+67
-7
lines changed

splunklib/binding.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def __new__(self, val='', skip_encode=False, encode_slash=False):
122122
elif skip_encode:
123123
return str.__new__(self, val)
124124
elif encode_slash:
125-
return str.__new__(self, urllib.quote(val, ''))
125+
return str.__new__(self, urllib.quote_plus(val))
126126
else:
127127
# When subclassing str, just call str's __new__ method
128128
# with your class and the value you want to have in the
@@ -529,7 +529,7 @@ def delete(self, path_segment, owner=None, app=None, sharing=None, **query):
529529

530530
@_authentication
531531
@_log_duration
532-
def get(self, path_segment, owner=None, app=None, sharing=None, **query):
532+
def get(self, path_segment="", owner=None, app=None, sharing=None, **query):
533533
"""Performs a GET operation from the REST path segment with the given
534534
namespace and query.
535535

splunklib/client.py

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,8 +1157,10 @@ def __getitem__(self, key):
11571157
# x[a,b] is translated to x.__getitem__( (a,b) ), so we
11581158
# have to extract values out.
11591159
key, ns = key
1160+
key = UrlEncoded(key, encode_slash=True)
11601161
response = self.get(key, owner=ns.owner, app=ns.app)
11611162
else:
1163+
key = UrlEncoded(key, encode_slash=True)
11621164
response = self.get(key)
11631165
entries = self._load_list(response)
11641166
if len(entries) > 1:
@@ -1193,6 +1195,7 @@ def __iter__(self, **kwargs):
11931195
for entity in saved_searches:
11941196
print "Saved search named %s" % entity.name
11951197
"""
1198+
11961199
for item in self.iter(**kwargs):
11971200
yield item
11981201

@@ -1399,6 +1402,8 @@ def list(self, count=None, **kwargs):
13991402
return list(self.iter(count=count, **kwargs))
14001403

14011404

1405+
1406+
14021407
class Collection(ReadOnlyCollection):
14031408
"""A collection of entities.
14041409
@@ -1444,6 +1449,7 @@ class Collection(ReadOnlyCollection):
14441449
:class:`Collection` does no caching. Each call makes at least one
14451450
round trip to the server to fetch data.
14461451
"""
1452+
14471453
def create(self, name, **params):
14481454
"""Creates a new entity in this collection.
14491455
@@ -1518,7 +1524,7 @@ def delete(self, name, **params):
15181524
saved_searches.delete('my_saved_search')
15191525
assert 'my_saved_search' not in saved_searches
15201526
"""
1521-
1527+
name = UrlEncoded(name, encode_slash=True)
15221528
if 'namespace' in params:
15231529
namespace = params.pop('namespace')
15241530
params['owner'] = namespace.owner
@@ -1534,7 +1540,57 @@ def delete(self, name, **params):
15341540
raise KeyError("No such entity %s" % name)
15351541
else:
15361542
raise
1537-
return self
1543+
return
1544+
1545+
def get(self, name="", owner=None, app=None, sharing=None, **query):
1546+
"""Performs a GET request to the server on the collection.
1547+
1548+
If *owner*, *app*, and *sharing* are omitted, this method takes a
1549+
default namespace from the :class:`Service` object for this :class:`Endpoint`.
1550+
All other keyword arguments are included in the URL as query parameters.
1551+
1552+
:raises AuthenticationError: Raised when the ``Service`` is not logged in.
1553+
:raises HTTPError: Raised when an error in the request occurs.
1554+
:param path_segment: A path segment relative to this endpoint.
1555+
:type path_segment: ``string``
1556+
:param owner: The owner context of the namespace (optional).
1557+
:type owner: ``string``
1558+
:param app: The app context of the namespace (optional).
1559+
:type app: ``string``
1560+
:param sharing: The sharing mode for the namespace (optional).
1561+
:type sharing: "global", "system", "app", or "user"
1562+
:param query: All other keyword arguments, which are used as query
1563+
parameters.
1564+
:type query: ``string``
1565+
:return: The response from the server.
1566+
:rtype: ``dict`` with keys ``body``, ``headers``, ``reason``,
1567+
and ``status``
1568+
1569+
Example:
1570+
1571+
import splunklib.client
1572+
s = client.service(...)
1573+
saved_searches = s.saved_searches
1574+
saved_searches.get("my/saved/search") == \\
1575+
{'body': ...a response reader object...,
1576+
'headers': [('content-length', '26208'),
1577+
('expires', 'Fri, 30 Oct 1998 00:00:00 GMT'),
1578+
('server', 'Splunkd'),
1579+
('connection', 'close'),
1580+
('cache-control', 'no-store, max-age=0, must-revalidate, no-cache'),
1581+
('date', 'Fri, 11 May 2012 16:30:35 GMT'),
1582+
('content-type', 'text/xml; charset=utf-8')],
1583+
'reason': 'OK',
1584+
'status': 200}
1585+
saved_searches.get('nonexistant/search') # raises HTTPError
1586+
s.logout()
1587+
saved_searches.get() # raises AuthenticationError
1588+
1589+
"""
1590+
name = UrlEncoded(name, encode_slash=True)
1591+
return super(Collection, self).get(name, owner, app, sharing, **query)
1592+
1593+
15381594

15391595

15401596
class ConfigurationFile(Collection):
@@ -1962,6 +2018,7 @@ def __getitem__(self, key):
19622018
if isinstance(key, tuple) and len(key) == 2:
19632019
# Fetch a single kind
19642020
key, kind = key
2021+
key = UrlEncoded(key, encode_slash=True)
19652022
try:
19662023
response = self.get(self.kindpath(kind) + "/" + key)
19672024
entries = self._load_list(response)
@@ -1980,6 +2037,7 @@ def __getitem__(self, key):
19802037
# Iterate over all the kinds looking for matches.
19812038
kind = None
19822039
candidate = None
2040+
key = UrlEncoded(key, encode_slash=True)
19832041
for kind in self.kinds:
19842042
try:
19852043
response = self.get(kind + "/" + key)
@@ -2213,12 +2271,12 @@ def kindpath(self, kind):
22132271
:rtype: ``string``
22142272
"""
22152273
if kind in self.kinds:
2216-
return kind
2274+
return UrlEncoded(kind, skip_encode=True)
22172275
# Special cases
22182276
elif kind == 'tcp':
2219-
return 'tcp/raw'
2277+
return UrlEncoded('tcp/raw', skip_encode=True)
22202278
elif kind == 'splunktcp':
2221-
return 'tcp/cooked'
2279+
return UrlEncoded('tcp/cooked', skip_encode=True)
22222280
else:
22232281
raise ValueError("No such kind on server: %s" % kind)
22242282

@@ -2285,6 +2343,7 @@ def list(self, *kinds, **kwargs):
22852343
path = self.kindpath(kind)
22862344
logging.debug("Path for inputs: %s", path)
22872345
try:
2346+
path = UrlEncoded(path, skip_encode=True)
22882347
response = self.get(path, **kwargs)
22892348
except HTTPError, he:
22902349
if he.status == 404: # No inputs of this kind
@@ -2309,6 +2368,7 @@ def list(self, *kinds, **kwargs):
23092368
for kind in kinds:
23102369
response = None
23112370
try:
2371+
kind = UrlEncoded(kind, skip_encode=True)
23122372
response = self.get(self.kindpath(kind), search=search)
23132373
except HTTPError as e:
23142374
if e.status == 404:

0 commit comments

Comments
 (0)