From b4ae16acc669f45d3fbf4aee1ee3db3148de7868 Mon Sep 17 00:00:00 2001 From: David Rousselie Date: Wed, 7 Feb 2007 18:33:37 +0100 Subject: [PATCH] Multi account types support : change discovery handling In discovery process, account types are listed with jid darcs-hash:20070207173337-86b55-b1b67a8d099e3ab19caa7599953c6836339db6ad.gz --- run_tests.py | 2 +- src/jcl/jabber/component.py | 81 ++++++++++------- tests/jcl/jabber/test_component.py | 141 +++++++++++++++++++++++++---- tests/jcl/model/account.py | 2 +- 4 files changed, 174 insertions(+), 52 deletions(-) diff --git a/run_tests.py b/run_tests.py index e15aa87..6cdae74 100644 --- a/run_tests.py +++ b/run_tests.py @@ -62,7 +62,7 @@ if __name__ == '__main__': jcl_suite = unittest.TestSuite() # jcl_suite.addTest(FeederComponent_TestCase('test_handle_tick')) -# jcl_suite.addTest(JCLComponent_TestCase('test_disco_get_items_2types_with_node2')) +# jcl_suite.addTest(JCLComponent_TestCase('test_handle_get_register_new_type2')) # jcl_suite = unittest.TestSuite((component_suite)) # jcl_suite = unittest.TestSuite((presence_account_suite)) jcl_suite = unittest.TestSuite((component_suite, \ diff --git a/src/jcl/jabber/component.py b/src/jcl/jabber/component.py index 0d6ac8e..1752ff3 100644 --- a/src/jcl/jabber/component.py +++ b/src/jcl/jabber/component.py @@ -268,24 +268,36 @@ class JCLComponent(Component, object): self.__logger.debug("DISCO_GET_ITEMS") base_from_jid = unicode(info_query.get_from().bare()) disco_items = DiscoItems() - if not node: - if len(self.account_classes) == 1: - self._list_accounts(disco_items, self.account_classes[0], base_from_jid) - else: + if not node: # first level + if len(self.account_classes) == 1: # list accounts with only one type declared + regexp_type = re.compile("(.*)Account$") + match = regexp_type.search(self.account_classes[0].__name__) + if match is not None: + account_type = match.group(1) + self._list_accounts(disco_items, self.account_classes[0], \ + base_from_jid, account_type) + else: + print >>sys.stderr, self.account_classes[0].__name__ + \ + " name not well formed" + else: # list account types (when multiples accounts types) for account_class in self.account_classes: regexp_type = re.compile("(.*)Account$") match = regexp_type.search(account_class.__name__) if match is not None: account_type = match.group(1) + item_jid = JID(unicode(self.jid) + "/" + account_type) DiscoItem(disco_items, \ - self.jid, \ + item_jid, \ account_type, \ account_type) - else: + else: + print >>sys.stderr, account_class.__name__ + \ + " name not well formed" + else: # second level nodes = node.split("/"); if len(nodes) == 1 \ - and len(self.account_classes) > 1: - self.__logger.debug("Listing account for " + nodes[0]) # for p1 TODO add type to get_items + and len(self.account_classes) > 1: # second level only with multiples account types + self.__logger.debug("Listing account for " + nodes[0]) account_class = self._get_account_class(nodes[0] + "Account") if account_class is not None: self._list_accounts(disco_items, \ @@ -312,37 +324,39 @@ class JCLComponent(Component, object): """Send back register form to user see node structure in disco_get_items() """ - self.__logger.debug("GET_REGISTER") - lang_class = self.lang.get_lang_class_from_node(info_query.get_node()) - base_from_jid = unicode(info_query.get_from().bare()) - to_jid = info_query.get_to() - info_query = info_query.make_result_response() - query = info_query.new_query("jabber:iq:register") - if to_jid.node is not None: - node_list = to_jid.node.split("/") - name = None - if len(node_list) == 2: - _account_class = self._get_account_class(node_list[0] + "Account") - name = node_list[1] - else: - if len(node_list) == 1: - if len(self.account_classes) == 1: - _account_class = self.account_classes[0] - name = node_list[0] - else: - _account_class = self._get_account_class(node_list[0] + "Account") - self.get_reg_form(lang_class, \ - _account_class).attach_xml(query) + def get_reg_form_for_account(_account_class, name, base_from_jid): self.db_connect() + # TODO : do it only one time for _account in _account_class.select(\ AND(_account_class.q.name == name, \ _account_class.q.user_jid == base_from_jid)): self.get_reg_form_init(lang_class, \ _account).attach_xml(query) self.db_disconnect() + + self.__logger.debug("GET_REGISTER") + lang_class = self.lang.get_lang_class_from_node(info_query.get_node()) + base_from_jid = unicode(info_query.get_from().bare()) + to_jid = info_query.get_to() + info_query = info_query.make_result_response() + query = info_query.new_query("jabber:iq:register") + if to_jid.resource is None: + account_type = "" else: + account_type = to_jid.resource + if to_jid.node is not None: + # get_register on an existing account of type resource + "Account" + get_reg_form_for_account(self._get_account_class(account_type + "Account"), \ + to_jid.node, \ + base_from_jid) + else: + if to_jid.resource is None: # get_register on main entity (1 account type) + _account_class = self.account_classes[0] + else: # get_register new account of type node_list[0] + "Account" + _account_class = self._get_account_class(account_type + "Account") self.get_reg_form(lang_class, \ - self.account_classes[0]).attach_xml(query) + _account_class).attach_xml(query) + self.stream.send(info_query) return 1 @@ -652,14 +666,17 @@ class JCLComponent(Component, object): def _list_accounts(self, disco_items, _account_class, base_from_jid, account_type = ""): """List accounts in disco_items for given _account_class and user jid""" - if account_type != "": + if account_type is not None and account_type != "": + resource = "/" + account_type account_type = account_type + "/" + else: + resource = "" self.db_connect() for _account in _account_class.select(_account_class.q.user_jid == \ base_from_jid): self.__logger.debug(str(_account)) DiscoItem(disco_items, \ - JID(_account.jid), \ + JID(unicode(_account.jid) + resource), \ account_type + _account.name, \ _account.long_name) self.db_disconnect() diff --git a/tests/jcl/jabber/test_component.py b/tests/jcl/jabber/test_component.py index b372dab..ed7e36d 100644 --- a/tests/jcl/jabber/test_component.py +++ b/tests/jcl/jabber/test_component.py @@ -33,6 +33,7 @@ from sqlobject import * from sqlobject.dbconnection import TheURIOpener import pyxmpp.error as error +from pyxmpp.jid import JID from pyxmpp.iq import Iq from pyxmpp.stanza import Stanza from pyxmpp.presence import Presence @@ -114,6 +115,9 @@ class MockStreamNoConnect(MockStream): self.eof = True class JCLComponent_TestCase(unittest.TestCase): + ########################################################################### + # Utility methods + ########################################################################### def setUp(self): if os.path.exists(DB_PATH): os.unlink(DB_PATH) @@ -141,11 +145,17 @@ class JCLComponent_TestCase(unittest.TestCase): if os.path.exists(DB_PATH): os.unlink(DB_PATH) + ########################################################################### + # Constructor tests + ########################################################################### def test_constructor(self): account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) self.assertTrue(Account._connection.tableExists("account")) del account.hub.threadConnection + ########################################################################### + # 'run' tests + ########################################################################### def __comp_run(self): try: self.comp.run() @@ -245,6 +255,9 @@ class JCLComponent_TestCase(unittest.TestCase): self.assertEquals(presence.get_to(), "test2@test.com") self.assertEquals(presence.get_node().prop("type"), "unavailable") + ########################################################################### + # 'time_handler' tests + ########################################################################### def __handle_tick_test_time_handler(self): self.max_tick_count -= 1 if self.max_tick_count == 0: @@ -260,6 +273,9 @@ class JCLComponent_TestCase(unittest.TestCase): self.assertEquals(self.max_tick_count, 0) self.assertFalse(self.comp.running) + ########################################################################### + # 'authenticated handler' tests + ########################################################################### def test_authenticated_handler(self): self.comp.stream = MockStream() self.comp.authenticated() @@ -306,32 +322,50 @@ class JCLComponent_TestCase(unittest.TestCase): self.assertEquals(presence.get_to(), "test2@test.com") self.assertEquals(presence.get_node().prop("type"), "probe") + ########################################################################### + # 'signal_handler' tests + ########################################################################### def test_signal_handler(self): self.comp.running = True self.comp.signal_handler(42, None) self.assertFalse(self.comp.running) + ########################################################################### + # 'disco_get_info' tests + ########################################################################### def test_disco_get_info(self): disco_info = self.comp.disco_get_info(None, None) self.assertTrue(disco_info.has_feature("jabber:iq:version")) self.assertTrue(disco_info.has_feature("jabber:iq:register")) + def test_disco_get_info_multiple_account_type(self): + self.comp.account_classes = [ExampleAccount, Example2Account] + disco_info = self.comp.disco_get_info(None, None) + self.assertTrue(disco_info.has_feature("jabber:iq:version")) + self.assertFalse(disco_info.has_feature("jabber:iq:register")) + def test_disco_get_info_node(self): disco_info = self.comp.disco_get_info("node_test", None) self.assertTrue(disco_info.has_feature("jabber:iq:register")) def test_disco_get_info_long_node(self): + self.comp.account_classes = [ExampleAccount, Example2Account] disco_info = self.comp.disco_get_info("node_type/node_test", None) self.assertTrue(disco_info.has_feature("jabber:iq:register")) + ########################################################################### + # 'disco_get_items' tests + ########################################################################### def test_disco_get_items_1type_no_node(self): + """get_items on main entity. Must list accounts""" account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) account1 = Account(user_jid = "user1@test.com", \ name = "account1", \ jid = "account1@jcl.test.com") del account.hub.threadConnection info_query = Iq(stanza_type = "get", \ - from_jid = "user1@test.com") + from_jid = "user1@test.com", \ + to_jid = "jcl.test.com") disco_items = self.comp.disco_get_items(None, info_query) self.assertEquals(len(disco_items.get_items()), 1) disco_item = disco_items.get_items()[0] @@ -340,17 +374,20 @@ class JCLComponent_TestCase(unittest.TestCase): self.assertEquals(disco_item.get_name(), account1.long_name) def test_disco_get_items_1type_with_node(self): + """get_items on an account. Must return nothing""" account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) account1 = Account(user_jid = "user1@test.com", \ name = "account1", \ jid = "account1@jcl.test.com") del account.hub.threadConnection info_query = Iq(stanza_type = "get", \ - from_jid = "user1@test.com") + from_jid = "user1@test.com", \ + to_jid = "account1@jcl.test.com") disco_items = self.comp.disco_get_items("account1", info_query) self.assertEquals(disco_items.get_items(), []) def test_disco_get_items_2types_no_node(self): + """get_items on main entity. Must account types""" self.comp.account_classes = [ExampleAccount, Example2Account] account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) account11 = ExampleAccount(user_jid = "user1@test.com", \ @@ -361,21 +398,24 @@ class JCLComponent_TestCase(unittest.TestCase): jid = "account21@jcl.test.com") del account.hub.threadConnection info_query = Iq(stanza_type = "get", \ - from_jid = "user1@test.com") + from_jid = "user1@test.com", \ + to_jid = "jcl.test.com") disco_items = self.comp.disco_get_items(None, info_query) self.assertEquals(len(disco_items.get_items()), 2) disco_item = disco_items.get_items()[0] - self.assertEquals(disco_item.get_jid(), self.comp.jid) + self.assertEquals(unicode(disco_item.get_jid()), unicode(self.comp.jid) + "/Example") self.assertEquals(disco_item.get_node(), "Example") self.assertEquals(disco_item.get_name(), "Example") disco_item = disco_items.get_items()[1] - self.assertEquals(disco_item.get_jid(), self.comp.jid) + self.assertEquals(unicode(disco_item.get_jid()), unicode(self.comp.jid) + "/Example2") self.assertEquals(disco_item.get_node(), "Example2") self.assertEquals(disco_item.get_name(), "Example2") # Be careful, account_classes cannot contains parent classes # def test_disco_get_items_2types_with_node(self): + """get_items on the first account type node. Must return account list of + that type for the current user""" self.comp.account_classes = [ExampleAccount, Example2Account] account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) account11 = ExampleAccount(user_jid = "user1@test.com", \ @@ -392,15 +432,18 @@ class JCLComponent_TestCase(unittest.TestCase): jid = "account22@jcl.test.com") del account.hub.threadConnection info_query = Iq(stanza_type = "get", \ - from_jid = "user1@test.com") + from_jid = "user1@test.com", \ + to_jid = "jcl.test.com/Example") disco_items = self.comp.disco_get_items("Example", info_query) self.assertEquals(len(disco_items.get_items()), 1) disco_item = disco_items.get_items()[0] - self.assertEquals(disco_item.get_jid(), account11.jid) + self.assertEquals(unicode(disco_item.get_jid()), unicode(account11.jid) + "/Example") self.assertEquals(disco_item.get_node(), "Example/" + account11.name) self.assertEquals(disco_item.get_name(), account11.long_name) def test_disco_get_items_2types_with_node2(self): + """get_items on the second account type node. Must return account list of + that type for the current user""" self.comp.account_classes = [ExampleAccount, Example2Account] account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) account11 = ExampleAccount(user_jid = "user1@test.com", \ @@ -417,15 +460,17 @@ class JCLComponent_TestCase(unittest.TestCase): jid = "account22@jcl.test.com") del account.hub.threadConnection info_query = Iq(stanza_type = "get", \ - from_jid = "user2@test.com") + from_jid = "user2@test.com", \ + to_jid = "jcl.test.com/Example2") disco_items = self.comp.disco_get_items("Example2", info_query) self.assertEquals(len(disco_items.get_items()), 1) disco_item = disco_items.get_items()[0] - self.assertEquals(disco_item.get_jid(), account22.jid) + self.assertEquals(unicode(disco_item.get_jid()), unicode(account22.jid) + "/Example2") self.assertEquals(disco_item.get_node(), "Example2/" + account22.name) self.assertEquals(disco_item.get_name(), account22.long_name) def test_disco_get_items_2types_with_long_node(self): + """get_items on a first type account. Must return nothing""" self.comp.account_classes = [ExampleAccount, Example2Account] account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) account1 = ExampleAccount(user_jid = "user1@test.com", \ @@ -433,11 +478,13 @@ class JCLComponent_TestCase(unittest.TestCase): jid = "account1@jcl.test.com") del account.hub.threadConnection info_query = Iq(stanza_type = "get", \ - from_jid = "user1@test.com") + from_jid = "user1@test.com", \ + to_jid = "account1@jcl.test.com/Example") disco_items = self.comp.disco_get_items("Example/account1", info_query) self.assertEquals(disco_items.get_items(), []) def test_disco_get_items_2types_with_long_node2(self): + """get_items on a second type account. Must return nothing""" self.comp.account_classes = [ExampleAccount, Example2Account] account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL) account1 = Example2Account(user_jid = "user1@test.com", \ @@ -445,10 +492,14 @@ class JCLComponent_TestCase(unittest.TestCase): jid = "account1@jcl.test.com") del account.hub.threadConnection info_query = Iq(stanza_type = "get", \ - from_jid = "user1@test.com") + from_jid = "user1@test.com", \ + to_jid = "account1@jcl.test.com/Example2") disco_items = self.comp.disco_get_items("Example2/account1", info_query) self.assertEquals(disco_items.get_items(), []) + ########################################################################### + # 'handle_get_version' tests + ########################################################################### def test_handle_get_version(self): self.comp.stream = MockStream() self.comp.stream_class = MockStream @@ -467,6 +518,9 @@ class JCLComponent_TestCase(unittest.TestCase): self.assertEquals(len(version_nodes), 1) self.assertEquals(version_nodes[0].content, self.comp.version) + ########################################################################### + # 'handle_get_register' tests + ########################################################################### def test_handle_get_register_new(self): self.comp.stream = MockStream() self.comp.stream_class = MockStream @@ -497,13 +551,7 @@ class JCLComponent_TestCase(unittest.TestCase): self.assertEquals(fields[0].prop("label"), Lang.en.account_name) self.assertEquals(fields[0].children.name, "required") - def test_handle_get_register_new_complex(self): - self.comp.stream = MockStream() - self.comp.stream_class = MockStream - self.comp.account_classes = [ExampleAccount] - self.comp.handle_get_register(Iq(stanza_type = "get", \ - from_jid = "user1@test.com", \ - to_jid = "jcl.test.com")) + def __check_get_register_new_type(self): self.assertEquals(len(self.comp.stream.sent), 1) iq_sent = self.comp.stream.sent[0] self.assertEquals(iq_sent.get_to(), "user1@test.com") @@ -562,6 +610,15 @@ class JCLComponent_TestCase(unittest.TestCase): self.assertEquals(fields[5].prop("type"), "text-single") self.assertEquals(fields[5].prop("var"), "test_int") self.assertEquals(fields[5].prop("label"), "test_int") + + def test_handle_get_register_new_complex(self): + self.comp.stream = MockStream() + self.comp.stream_class = MockStream + self.comp.account_classes = [ExampleAccount] + self.comp.handle_get_register(Iq(stanza_type = "get", \ + from_jid = "user1@test.com", \ + to_jid = "jcl.test.com")) + self.__check_get_register_new_type() def test_handle_get_register_exist(self): self.comp.stream = MockStream() @@ -714,6 +771,54 @@ class JCLComponent_TestCase(unittest.TestCase): self.assertEquals(field.children.name, "value") self.assertEquals(field.children.content, "21") + def test_handle_get_register_new_type1(self): + self.comp.stream = MockStream() + self.comp.stream_class = MockStream + self.comp.account_classes = [ExampleAccount, Example2Account] + self.comp.handle_get_register(Iq(stanza_type = "get", \ + from_jid = "user1@test.com", \ + to_jid = "jcl.test.com/example")) + self.__check_get_register_new_type() + + # TODO + def test_handle_get_register_new_type2(self): + self.comp.stream = MockStream() + self.comp.stream_class = MockStream + self.comp.account_classes = [ExampleAccount, Example2Account] + self.comp.handle_get_register(Iq(stanza_type = "get", \ + from_jid = JID("user1@test.com"), \ + to_jid = JID("jcl.test.com/example2"))) + self.assertEquals(len(self.comp.stream.sent), 1) + iq_sent = self.comp.stream.sent[0] + self.assertEquals(iq_sent.get_to(), "user1@test.com") + titles = iq_sent.xpath_eval("jir:query/jxd:x/jxd:title", \ + {"jir" : "jabber:iq:register", \ + "jxd" : "jabber:x:data"}) + self.assertEquals(len(titles), 1) + self.assertEquals(titles[0].content, \ + Lang.en.register_title) + instructions = iq_sent.xpath_eval("jir:query/jxd:x/jxd:instructions", \ + {"jir" : "jabber:iq:register", \ + "jxd" : "jabber:x:data"}) + self.assertEquals(len(instructions), 1) + self.assertEquals(instructions[0].content, \ + Lang.en.register_instructions) + fields = iq_sent.xpath_eval("jir:query/jxd:x/jxd:field", \ + {"jir" : "jabber:iq:register", \ + "jxd" : "jabber:x:data"}) + self.assertEquals(len(fields), 2) + self.assertEquals(fields[0].prop("type"), "text-single") + self.assertEquals(fields[0].prop("var"), "name") + self.assertEquals(fields[0].prop("label"), Lang.en.account_name) + self.assertEquals(fields[0].children.name, "required") + + self.assertEquals(fields[1].prop("type"), "text-single") + self.assertEquals(fields[1].prop("var"), "test_new_int") + self.assertEquals(fields[1].prop("label"), "test_new_int") + + ########################################################################### + # 'handle_set_register' tests + ########################################################################### def test_handle_set_register_new(self): self.comp.stream = MockStream() self.comp.stream_class = MockStream diff --git a/tests/jcl/model/account.py b/tests/jcl/model/account.py index 1bc9b97..bb83d0b 100644 --- a/tests/jcl/model/account.py +++ b/tests/jcl/model/account.py @@ -63,7 +63,7 @@ class Example2Account(Account): def _get_register_fields(cls): return Account.get_register_fields() + \ - [("test_new_int", "text-single", None, account_int_post_func, \ + [("test_new_int", "text-single", None, account.int_post_func, \ lambda field_name: 43)] get_register_fields = classmethod(_get_register_fields)