diff --git a/pyotrs/lib.py b/pyotrs/lib.py index 92114d0fee8147e6dde40e09adc562c2c0eeacf0..c070a3d904b1d5c835a0930485e0539cc582c6bf 100644 --- a/pyotrs/lib.py +++ b/pyotrs/lib.py @@ -1421,7 +1421,7 @@ class Client(object): if not self.session_id_store.value: raise SessionNotCreated("Call session_create() or " "session_restore_or_set_up_new() first") - self.operation = "PublicCategoryList " + self.operation = "PublicCategoryList" payload = { "SessionID": self.session_id_store.value @@ -1432,7 +1432,17 @@ class Client(object): return self.result def faq_public_faq_get(self, item_ids=None, attachment_contents=True): - """faq_public_category_list""" + """faq_public_category_list + + Args: + item_ids (list): list of item IDs + attachment_contents (bool): whether to retrieve content of FAQ attachments + + Returns: + **list**: of **dict** containing FAQ data + + """ + if not self.session_id_store.value: raise SessionNotCreated("Call session_create() or " "session_restore_or_set_up_new() first") @@ -1454,20 +1464,65 @@ class Client(object): if not attachment_contents: payload.update({"GetAttachmentContents": 0}) - return self._parse_and_validate_response(self._send_request(payload)) + if self._parse_and_validate_response(self._send_request(payload)): + return self.result + + def faq_public_faq_search(self, what=None, number=None, title=None, search_dict=None): + """faq_public_category_list + + Args: + what (str): + number (str): + title (str): + search_dict (dict): + + Returns: + **list**: of found FAQ item IDs + + # Original documentation: + # perform PublicFAQSearch Operation. This will return a list of public FAQ entries. + # Number = > '*134*', # (optional) + # Title = > '*some title*', # (optional) + # + # # is searching in Number, Title, Keyword and Field1-6 + # What = > '*some text*', # (optional) + # + # Keyword = > '*webserver*', # (optional) + # LanguageIDs = > [4, 5, 6], # (optional) + # CategoryIDs = > [7, 8, 9], # (optional) + + """ - def faq_public_faq_search(self): - """faq_public_category_list""" if not self.session_id_store.value: raise SessionNotCreated("Call session_create() or " "session_restore_or_set_up_new() first") self.operation = "PublicFAQSearch" payload = { - "SessionID": self.session_id_store.value + "SessionID": self.session_id_store.value, } - return self._parse_and_validate_response(self._send_request(payload)) + if what: + payload.update({"What": what}) + + if number: + payload.update({"Number": number}) + + if title: + payload.update({"Title": title}) + + if search_dict: + if not isinstance(search_dict, dict): + raise ArgumentInvalidError("Expecting dict for search_dict!") + payload.update(search_dict) + + if self._parse_and_validate_response(self._send_request(payload)): + if not self.result: + return [] + elif len(self.result) == 1: + return [self.result] + else: + return self.result """ GenericInterface::Operation::Link::LinkAdd @@ -1761,8 +1816,6 @@ class Client(object): elif route in self.routes_link: self._url = ("{0}/otrs/nph-genericinterface.pl/Webservice/" "{1}{2}".format(self.baseurl, self.ws_link, route)) - else: - raise NotImplementedError("could not _build_url for {0}".format(self)) return self._url @@ -1876,6 +1929,17 @@ class Client(object): self.result = _link_list return True + # PublicFAQSearch result can be empty + if self.operation in "PublicFAQSearch": + _public_faq_search_result_list = self.result_json.get(self._result_type, None) + if not _public_faq_search_result_list: + if self.result_json["Error"]["ErrorCode"] == "PublicFAQSearch.NotFAQData": + self.result = [] + return True + else: + self.result = _public_faq_search_result_list + return True + # now handle other operations if self.result_json.get(self._result_type, None): self._result_error = False diff --git a/tests/test_article.py b/tests/test_article.py index f642188fe137a93430fcedf6252eeff5da760d52..7a7ba7bce909c431dc09b90fbcd3da51dbb5a7df 100644 --- a/tests/test_article.py +++ b/tests/test_article.py @@ -453,6 +453,7 @@ class ArticleTests(unittest.TestCase): def main(): unittest.main() + if __name__ == '__main__': main() diff --git a/tests/test_attachment.py b/tests/test_attachment.py index 2195a0e05c145106046edf6bacdde749eb3aa5f6..031a039ac000ea96050ffbfeba373de0c9d33e26 100644 --- a/tests/test_attachment.py +++ b/tests/test_attachment.py @@ -127,6 +127,7 @@ class AttachmentTests(unittest.TestCase): def main(): unittest.main() + if __name__ == '__main__': main() diff --git a/tests/test_client.py b/tests/test_client.py index 06c54e7b01380e113eda02000cbb4a05bcd3c4ff..d543d1be918129a63a9d31fb6c4a3abe145e3b06 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -954,18 +954,34 @@ class ClientTests(unittest.TestCase): 'Call session_create.*', obj.faq_public_faq_get) - def test_faq_public_faq_get_no_item_ids(self): - """Test faq_public_faq_get - no item_ids""" + def test_faq_public_faq_get_no_item_id(self): + """Test faq_public_faq_get - no item id""" obj = Client(baseurl="http://fqdn") obj.session_id_store.value = "some_session_id" + self.assertRaisesRegex(ArgumentMissingError, 'item_ids is required', obj.faq_public_faq_get) @mock.patch('pyotrs.Client._send_request') @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True) - def test_faq_public_faq_get_list_of_ids(self, mock_parse_validate, mock_send_req): - """Tests faq_public_faq_get - ok""" + def test_faq_public_faq_get(self, mock_parse_validate, mock_send_req): + """Tests faq_public_faq_get - with str, no attachment content - ok""" + # create object + obj = Client(baseurl="http://fqdn") + obj.session_id_store.value = "some_session_id" + mock_parse_validate.return_value = True + mock_send_req.return_value = "mock" + + obj.faq_public_faq_get("1", attachment_contents=False) + + self.assertEqual(mock_parse_validate.call_count, 1) + self.assertEqual(mock_send_req.call_count, 1) + + @mock.patch('pyotrs.Client._send_request') + @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True) + def test_faq_public_faq_get_list(self, mock_parse_validate, mock_send_req): + """Tests faq_public_faq_get - with list - ok """ # create object obj = Client(baseurl="http://fqdn") obj.session_id_store.value = "some_session_id" @@ -977,54 +993,80 @@ class ClientTests(unittest.TestCase): self.assertEqual(mock_parse_validate.call_count, 1) self.assertEqual(mock_send_req.call_count, 1) + def test_faq_public_faq_search_no_session_created(self): + """Test faq_public_faq_search - no session""" + obj = Client(baseurl="http://fqdn") + self.assertRaisesRegex(SessionNotCreated, + 'Call session_create.*', + obj.faq_public_faq_search) + + def test_faq_public_faq_search_invalid_search_dict(self): + """Test faq_public_faq_search - invalid search dict""" + obj = Client(baseurl="http://fqdn") + obj.session_id_store.value = "some_session_id" + self.assertRaisesRegex(ArgumentInvalidError, + 'Expecting dict for search_dict!', + obj.faq_public_faq_search, search_dict="this is no dict") + @mock.patch('pyotrs.Client._send_request') @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True) - def test_faq_public_faq_get_single_id(self, mock_parse_validate, mock_send_req): - """Tests faq_public_faq_get - ok""" + def test_faq_public_faq_search(self, mock_parse_validate, mock_send_req): + """Tests faq_public_faq_search - ok""" # create object obj = Client(baseurl="http://fqdn") obj.session_id_store.value = "some_session_id" mock_parse_validate.return_value = True mock_send_req.return_value = "mock" - obj.faq_public_faq_get(4711) + obj.faq_public_faq_search("foobar") self.assertEqual(mock_parse_validate.call_count, 1) self.assertEqual(mock_send_req.call_count, 1) @mock.patch('pyotrs.Client._send_request') @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True) - def test_faq_public_faq_get_list_no_content(self, mock_parse_validate, mock_send_req): - """Tests faq_public_faq_get - ok""" + def test_faq_public_faq_search_number(self, mock_parse_validate, mock_send_req): + """Tests faq_public_faq_search - number - ok""" # create object obj = Client(baseurl="http://fqdn") obj.session_id_store.value = "some_session_id" mock_parse_validate.return_value = True mock_send_req.return_value = "mock" + obj.result = [] - obj.faq_public_faq_get([1, 2], attachment_contents=False) + obj.faq_public_faq_search(number="21") self.assertEqual(mock_parse_validate.call_count, 1) self.assertEqual(mock_send_req.call_count, 1) - def test_faq_public_faq_search_no_session_created(self): - """Test faq_public_faq_search - no session""" + @mock.patch('pyotrs.Client._send_request') + @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True) + def test_faq_public_faq_search_title(self, mock_parse_validate, mock_send_req): + """Tests faq_public_faq_search - title - ok""" + # create object obj = Client(baseurl="http://fqdn") - self.assertRaisesRegex(SessionNotCreated, - 'Call session_create.*', - obj.faq_public_faq_search) + obj.session_id_store.value = "some_session_id" + mock_parse_validate.return_value = True + mock_send_req.return_value = "mock" + obj.result = [u'1'] + + obj.faq_public_faq_search(title="Test-Title") + + self.assertEqual(mock_parse_validate.call_count, 1) + self.assertEqual(mock_send_req.call_count, 1) @mock.patch('pyotrs.Client._send_request') @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True) - def test_faq_public_faq_search(self, mock_parse_validate, mock_send_req): - """Tests faq_public_faq_search - ok""" + def test_faq_public_faq_search_search_dict(self, mock_parse_validate, mock_send_req): + """Tests faq_public_faq_search - search_dict - ok""" # create object obj = Client(baseurl="http://fqdn") obj.session_id_store.value = "some_session_id" mock_parse_validate.return_value = True mock_send_req.return_value = "mock" + obj.result = [u'3', u'4'] - obj.faq_public_faq_search() + obj.faq_public_faq_search(search_dict={"Keyword": "Password"}) self.assertEqual(mock_parse_validate.call_count, 1) self.assertEqual(mock_send_req.call_count, 1) @@ -1340,6 +1382,24 @@ class ClientTests(unittest.TestCase): "could not _build_url for.*", obj._build_url) + def test__build_url_faq_language_list(self): + """Test _build_url for faq_language_list""" + obj = Client(baseurl="http://fqdn") + obj.operation = "PublicCategoryList" + + self.assertEqual("http://fqdn/otrs/nph-genericinterface.pl/Webservice/" + "GenericFAQConnectorREST/PublicCategoryList", + obj._build_url()) + + def test__build_url_faq_category_list(self): + """Test _build_url for faq_category_list""" + obj = Client(baseurl="http://fqdn") + obj.operation = "LanguageList" + + self.assertEqual("http://fqdn/otrs/nph-genericinterface.pl/Webservice/" + "GenericFAQConnectorREST/LanguageList", + obj._build_url()) + def test__build_url_link_add(self): """Test _build_url for link_add""" obj = Client(baseurl="http://fqdn") @@ -1370,38 +1430,33 @@ class ClientTests(unittest.TestCase): def test__send_request_invalid_method(self): # ""Test _send_request with invalid http method "" - - my_ticket_connector_cfg = { + operation_mapping_invalid = { 'Name': 'GenericTicketConnectorREST', 'Config': { - 'SessionCreate': {'RequestMethod': 'POST', - 'Route': '/Session', - 'Result': 'SessionID'}, - 'TicketCreate': {'RequestMethod': 'POST', - 'Route': '/Ticket', - 'Result': 'TicketID'}, - 'TicketGet': {'RequestMethod': 'FOOBAR', - 'Route': '/Ticket/:TicketID', - 'Result': 'Ticket'}, - 'TicketGetList': {'RequestMethod': 'GET', - 'Route': '/TicketList', - 'Result': 'Ticket'}, - 'TicketSearch': {'RequestMethod': 'GET', - 'Route': '/Ticket', - 'Result': 'TicketID'}, - 'TicketUpdate': {'RequestMethod': 'PATCH', - 'Route': '/Ticket/:TicketID', - 'Result': 'TicketID'}, + 'SessionCreate': + {'Result': 'SessionID', 'RequestMethod': 'FB', 'Route': '/Session'}, + 'TicketCreate': + {'Result': 'TicketID', 'RequestMethod': 'FB', 'Route': '/Ticket'}, + 'TicketGet': + {'Result': 'Ticket', 'RequestMethod': 'FB', 'Route': '/Ticket/:TicketID'}, + 'TicketGetList': + {'Result': 'Ticket', 'RequestMethod': 'FB', 'Route': '/TicketList'}, + 'TicketSearch': + {'Result': 'TicketID', 'RequestMethod': 'FB', 'Route': '/Ticket'}, + 'TicketUpdate': + {'Result': 'TicketID', 'RequestMethod': 'FB', 'Route': '/Ticket/:TicketID'} } } - obj = Client(baseurl="http://fqdn", webservice_config_ticket=my_ticket_connector_cfg) - obj.operation = "TicketGet" + obj = Client(baseurl="http://fqdn", webservice_config_ticket=operation_mapping_invalid) + + obj.operation = "TicketSearch" + obj._result_type = obj.ws_config[obj.operation]["Result"] + self.assertRaisesRegex(ValueError, 'invalid http_method', obj._send_request, - payload={"foo": "bar"}, - ticket_id=1) + payload={"foo": "bar"}) @mock.patch('pyotrs.lib.requests.request') def test__send_request_ok(self, mock_requests_req): @@ -1595,6 +1650,39 @@ class ClientTests(unittest.TestCase): self.assertTrue(obj._result_error) self.assertDictEqual(obj.result_json, {u'FooBar': [u'1', u'3']}) + def test__validate_response_operation_faq_public_faq_search_ok(self): + """Test _validate_response with faq_public_faq_search ok""" + obj = Client(baseurl="http://localhost") + obj.operation = "PublicFAQSearch" + obj._result_type = obj.ws_config[obj.operation]["Result"] + + mocked_response = mock.Mock(spec=requests.Response) + mocked_response.status_code = 200 + mocked_response.json.return_value = {"ID": [u"3", u"2", u"1"]} + + obj._parse_and_validate_response(mocked_response) + self.assertEqual(obj._result_type, 'ID') + self.assertDictEqual(obj.result_json, {"ID": [u"3", u"2", u"1"]}) + + def test__validate_response_operation_faq_public_faq_search_no_result(self): + """Test _validate_response with faq_public_faq_search no result""" + obj = Client(baseurl="http://localhost") + obj.operation = "PublicFAQSearch" + obj._result_type = obj.ws_config[obj.operation]["Result"] + + mocked_response = mock.Mock(spec=requests.Response) + mocked_response.status_code = 200 + _dct = {u'Error': {u'ErrorCode': u'PublicFAQSearch.NotFAQData', + u'ErrorMessage': u'PublicFAQSearch: Could not get FAQ data in ' + u'Kernel::GenericInterface::Operation::FAQ::' + u'PublicFAQSearch::Run()'}} + mocked_response.json.return_value = _dct + + obj._parse_and_validate_response(mocked_response) + self.assertEqual(obj._result_type, 'ID') + + self.assertDictEqual(obj.result_json, _dct) + def test__validate_response_operation_link_add_ok(self): """Test _validate_response with link_add ok""" obj = Client(baseurl="http://localhost") @@ -1665,6 +1753,7 @@ class ClientTests(unittest.TestCase): def main(): unittest.main() + if __name__ == '__main__': main() diff --git a/tests/test_dynamic_field.py b/tests/test_dynamic_field.py index 642c361799d138ddcdb124356df1b31c495d5fc5..78004b7d8c985161a5566e9b8ead8eb5488a4ebc 100644 --- a/tests/test_dynamic_field.py +++ b/tests/test_dynamic_field.py @@ -97,6 +97,7 @@ class DynamicFieldTests(unittest.TestCase): def main(): unittest.main() + if __name__ == '__main__': main() diff --git a/tests/test_session_store.py b/tests/test_session_store.py index c1f6714aee37b2fc3c454b3984c03761e36c6910..53fca073cbf19280cb3f4d38a52e1c6b38a08f43 100644 --- a/tests/test_session_store.py +++ b/tests/test_session_store.py @@ -309,6 +309,7 @@ class SessionStoreTests(unittest.TestCase): def main(): unittest.main() + if __name__ == '__main__': main() diff --git a/tests/test_ticket.py b/tests/test_ticket.py index eb6bd14fc209d302791f10f7ba8efe152be74860..900536dfa6da470f922a74ad83607e7d53a9e8e3 100644 --- a/tests/test_ticket.py +++ b/tests/test_ticket.py @@ -610,6 +610,7 @@ class TicketTests(unittest.TestCase): def main(): unittest.main() + if __name__ == '__main__': main() diff --git a/tox.ini b/tox.ini index 6c006600e3af5aa8fa019fe16c82758f8fbf26d2..ac9ea849a076f018337258402ca0223f6ef40ad3 100644 --- a/tox.ini +++ b/tox.ini @@ -75,9 +75,9 @@ deps = commands = flake8 \ - --max-complexity=15 \ - --exclude=./build,.venv,.tox,dist,docs, \ - --ignore=Q000,E305 \ + --max-complexity=20 \ + --exclude=./build,.venv,.tox,.eggs,dist,docs, \ + --ignore=Q000 \ --max-line-length=99 \ []