@@ -862,6 +862,158 @@ def post(
862862 response = self .http .post (path , all_headers , ** query )
863863 return response
864864
865+ @_authentication
866+ @_log_duration
867+ def put (
868+ self ,
869+ path_segment ,
870+ owner = None ,
871+ app = None ,
872+ sharing = None ,
873+ headers = None ,
874+ ** query ,
875+ ):
876+ """Performs a PUT operation from the REST path segment with the given object,
877+ namespace and query.
878+
879+ This method is named to match the HTTP method. ``put`` makes at least
880+ one round trip to the server, one additional round trip for each 303
881+ status returned, and at most two additional round trips if
882+ the ``autologin`` field of :func:`connect` is set to ``True``.
883+
884+ If *owner*, *app*, and *sharing* are omitted, this method uses the
885+ default :class:`Context` namespace. All other keyword arguments are
886+ included in the URL as query parameters.
887+
888+ If you provide a ``body`` argument to ``put``, it will be used as the PUT body,
889+ and all other keyword arguments will be passed as GET-style arguments in the URL.
890+
891+ :raises AuthenticationError: Raised when the ``Context`` object is not
892+ logged in.
893+ :raises HTTPError: Raised when an error occurred in a PUT operation from
894+ *path_segment*.
895+ :param path_segment: A REST path segment.
896+ :type path_segment: ``string``
897+ :param owner: The owner context of the namespace (optional).
898+ :type owner: ``string``
899+ :param app: The app context of the namespace (optional).
900+ :type app: ``string``
901+ :param sharing: The sharing mode of the namespace (optional).
902+ :type sharing: ``string``
903+ :param headers: List of extra HTTP headers to send (optional).
904+ :type headers: ``list`` of 2-tuples.
905+ :param query: All other keyword arguments, which are used as query
906+ parameters.
907+ :param body: Parameters to be used in the put body. If specified,
908+ any parameters in the query will be applied to the URL instead of
909+ the body. If a dict is supplied, the key-value pairs will be form
910+ encoded. If a string is supplied, the body will be passed through
911+ in the request unchanged.
912+ :type body: ``dict`` or ``str``
913+ :return: The response from the server.
914+ :rtype: ``dict`` with keys ``body``, ``headers``, ``reason``,
915+ and ``status``
916+
917+ **Example**::
918+
919+ c = binding.connect(...)
920+ # Call an HTTP endpoint, exposed as Custom Rest Endpoint in a Splunk App.
921+ # PUT /servicesNS/-/app_name/custom_rest_endpoint
922+ c.put(
923+ app="app_name",
924+ path_segment="custom_rest_endpoint",
925+ body=json.dumps({"key": "val"}),
926+ headers=[("Content-Type", "application/json")],
927+ )
928+ """
929+ if headers is None :
930+ headers = []
931+
932+ path = self .authority + self ._abspath (
933+ path_segment , owner = owner , app = app , sharing = sharing
934+ )
935+
936+ logger .debug ("PUT request to %s (body: %s)" , path , mask_sensitive_data (query ))
937+ all_headers = headers + self .additional_headers + self ._auth_headers
938+ response = self .http .put (path , all_headers , ** query )
939+ return response
940+
941+ @_authentication
942+ @_log_duration
943+ def patch (
944+ self ,
945+ path_segment ,
946+ owner = None ,
947+ app = None ,
948+ sharing = None ,
949+ headers = None ,
950+ ** query ,
951+ ):
952+ """Performs a PATCH operation from the REST path segment with the given object,
953+ namespace and query.
954+
955+ This method is named to match the HTTP method. ``patch`` makes at least
956+ one round trip to the server, one additional round trip for each 303
957+ status returned, and at most two additional round trips if
958+ the ``autologin`` field of :func:`connect` is set to ``True``.
959+
960+ If *owner*, *app*, and *sharing* are omitted, this method uses the
961+ default :class:`Context` namespace. All other keyword arguments are
962+ included in the URL as query parameters.
963+
964+ If you provide a ``body`` argument to ``patch``, it will be used as the PATCH body,
965+ and all other keyword arguments will be passed as GET-style arguments in the URL.
966+
967+ :raises AuthenticationError: Raised when the ``Context`` object is not
968+ logged in.
969+ :raises HTTPError: Raised when an error occurred in a PATCH operation from
970+ *path_segment*.
971+ :param path_segment: A REST path segment.
972+ :type path_segment: ``string``
973+ :param owner: The owner context of the namespace (optional).
974+ :type owner: ``string``
975+ :param app: The app context of the namespace (optional).
976+ :type app: ``string``
977+ :param sharing: The sharing mode of the namespace (optional).
978+ :type sharing: ``string``
979+ :param headers: List of extra HTTP headers to send (optional).
980+ :type headers: ``list`` of 2-tuples.
981+ :param query: All other keyword arguments, which are used as query
982+ parameters.
983+ :param body: Parameters to be used in the patch body. If specified,
984+ any parameters in the query will be applied to the URL instead of
985+ the body. If a dict is supplied, the key-value pairs will be form
986+ encoded. If a string is supplied, the body will be passed through
987+ in the request unchanged.
988+ :type body: ``dict`` or ``str``
989+ :return: The response from the server.
990+ :rtype: ``dict`` with keys ``body``, ``headers``, ``reason``,
991+ and ``status``
992+
993+ **Example**::
994+
995+ c = binding.connect(...)
996+ # Call an HTTP endpoint, exposed as Custom Rest Endpoint in a Splunk App.
997+ # PATCH /servicesNS/-/app_name/custom_rest_endpoint
998+ c.patch(
999+ app="app_name",
1000+ path_segment="custom_rest_endpoint",
1001+ body=json.dumps({"key": "val"}),
1002+ headers=[("Content-Type", "application/json")],
1003+ )
1004+ """
1005+ if headers is None :
1006+ headers = []
1007+
1008+ path = self .authority + self ._abspath (
1009+ path_segment , owner = owner , app = app , sharing = sharing
1010+ )
1011+
1012+ logger .debug ("PATCH request to %s (body: %s)" , path , mask_sensitive_data (query ))
1013+ all_headers = headers + self .additional_headers + self ._auth_headers
1014+ response = self .http .patch (path , all_headers , ** query )
1015+ return response
1016+
8651017 @_authentication
8661018 @_log_duration
8671019 def request (
@@ -1305,6 +1457,40 @@ def __init__(
13051457 self .retries = retries
13061458 self .retryDelay = retryDelay
13071459
1460+ def _prepare_request_body_and_url (self , url , headers , ** kwargs ):
1461+ """Helper function to prepare the request body and URL.
1462+
1463+ :param url: The URL.
1464+ :type url: ``string``
1465+ :param headers: A list of pairs specifying the headers for the HTTP request.
1466+ :type headers: ``list``
1467+ :param kwargs: Additional keyword arguments (optional).
1468+ :type kwargs: ``dict``
1469+ :returns: A tuple containing the updated URL, headers, and body.
1470+ :rtype: ``tuple``
1471+ """
1472+ if headers is None :
1473+ headers = []
1474+
1475+ # We handle GET-style arguments and an unstructured body. This is here
1476+ # to support the receivers/stream endpoint.
1477+ if "body" in kwargs :
1478+ # We only use application/x-www-form-urlencoded if there is no other
1479+ # Content-Type header present. This can happen in cases where we
1480+ # send requests as application/json, e.g. for KV Store.
1481+ if len ([x for x in headers if x [0 ].lower () == "content-type" ]) == 0 :
1482+ headers .append (("Content-Type" , "application/x-www-form-urlencoded" ))
1483+
1484+ body = kwargs .pop ("body" )
1485+ if isinstance (body , dict ):
1486+ body = _encode (** body ).encode ("utf-8" )
1487+ if len (kwargs ) > 0 :
1488+ url = url + UrlEncoded ("?" + _encode (** kwargs ), skip_encode = True )
1489+ else :
1490+ body = _encode (** kwargs ).encode ("utf-8" )
1491+
1492+ return url , headers , body
1493+
13081494 def delete (self , url , headers = None , ** kwargs ):
13091495 """Sends a DELETE request to a URL.
13101496
@@ -1379,26 +1565,52 @@ def post(self, url, headers=None, **kwargs):
13791565 its structure).
13801566 :rtype: ``dict``
13811567 """
1382- if headers is None :
1383- headers = []
1568+ url , headers , body = self ._prepare_request_body_and_url (url , headers , ** kwargs )
1569+ message = {"method" : "POST" , "headers" : headers , "body" : body }
1570+ return self .request (url , message )
13841571
1385- # We handle GET-style arguments and an unstructured body. This is here
1386- # to support the receivers/stream endpoint.
1387- if "body" in kwargs :
1388- # We only use application/x-www-form-urlencoded if there is no other
1389- # Content-Type header present. This can happen in cases where we
1390- # send requests as application/json, e.g. for KV Store.
1391- if len ([x for x in headers if x [0 ].lower () == "content-type" ]) == 0 :
1392- headers .append (("Content-Type" , "application/x-www-form-urlencoded" ))
1572+ def put (self , url , headers = None , ** kwargs ):
1573+ """Sends a PUT request to a URL.
13931574
1394- body = kwargs .pop ("body" )
1395- if isinstance (body , dict ):
1396- body = _encode (** body ).encode ("utf-8" )
1397- if len (kwargs ) > 0 :
1398- url = url + UrlEncoded ("?" + _encode (** kwargs ), skip_encode = True )
1399- else :
1400- body = _encode (** kwargs ).encode ("utf-8" )
1401- message = {"method" : "POST" , "headers" : headers , "body" : body }
1575+ :param url: The URL.
1576+ :type url: ``string``
1577+ :param headers: A list of pairs specifying the headers for the HTTP
1578+ response (for example, ``[('Content-Type': 'text/cthulhu'), ('Token': 'boris')]``).
1579+ :type headers: ``list``
1580+ :param kwargs: Additional keyword arguments (optional). If the argument
1581+ is ``body``, the value is used as the body for the request, and the
1582+ keywords and their arguments will be URL encoded. If there is no
1583+ ``body`` keyword argument, all the keyword arguments are encoded
1584+ into the body of the request in the format ``x-www-form-urlencoded``.
1585+ :type kwargs: ``dict``
1586+ :returns: A dictionary describing the response (see :class:`HttpLib` for
1587+ its structure).
1588+ :rtype: ``dict``
1589+ """
1590+ url , headers , body = self ._prepare_request_body_and_url (url , headers , ** kwargs )
1591+ message = {"method" : "PUT" , "headers" : headers , "body" : body }
1592+ return self .request (url , message )
1593+
1594+ def patch (self , url , headers = None , ** kwargs ):
1595+ """Sends a PATCH request to a URL.
1596+
1597+ :param url: The URL.
1598+ :type url: ``string``
1599+ :param headers: A list of pairs specifying the headers for the HTTP
1600+ response (for example, ``[('Content-Type': 'text/cthulhu'), ('Token': 'boris')]``).
1601+ :type headers: ``list``
1602+ :param kwargs: Additional keyword arguments (optional). If the argument
1603+ is ``body``, the value is used as the body for the request, and the
1604+ keywords and their arguments will be URL encoded. If there is no
1605+ ``body`` keyword argument, all the keyword arguments are encoded
1606+ into the body of the request in the format ``x-www-form-urlencoded``.
1607+ :type kwargs: ``dict``
1608+ :returns: A dictionary describing the response (see :class:`HttpLib` for
1609+ its structure).
1610+ :rtype: ``dict``
1611+ """
1612+ url , headers , body = self ._prepare_request_body_and_url (url , headers , ** kwargs )
1613+ message = {"method" : "PATCH" , "headers" : headers , "body" : body }
14021614 return self .request (url , message )
14031615
14041616 def request (self , url , message , ** kwargs ):
0 commit comments