XML-RPC Integration with Malone =============================== Malone provides an XML-RPC interface for filing bugs. >>> import xmlrpclib >>> from lp.testing.xmlrpc import XMLRPCTestTransport >>> filebug_api = xmlrpclib.ServerProxy( ... 'http://test@canonical.com:test@xmlrpc.launchpad.dev/bugs/', ... transport=XMLRPCTestTransport()) The filebug API --------------- The filebug API is: filebug_api.filebug(params) params is a dict, with the following keys: REQUIRED ARGS: summary: A string comment: A string OPTIONAL ARGS: product: The product name, as a string. Default None. distro: The distro name, as a string. Default None. package: A string, allowed only if distro is specified. Default None. security_related: Is this a security vulnerability? Default False. subscribers: A list of email addresses. Default None. Either product or distro must be provided. The bug owner is the currently authenticated user, taken from the request. The return value is the bug URL, in short form, e.g.: http://launchpad.net/bugs/42 Support for attachments will be added in the near future. Examples -------- First, let's define a simple event listener to show that the IObjectCreatedEvent is being published when a bug is reported through the XML-RPC interface. >>> from canonical.lazr.testing.event import TestEventListener >>> from lazr.lifecycle.interfaces import IObjectCreatedEvent >>> from lp.bugs.interfaces.bug import IBug >>> def on_created_event(obj, event): ... print "ObjectCreatedEvent: %r" % obj >>> on_created_listener = TestEventListener( ... IBug, IObjectCreatedEvent, on_created_event) Reporting a product bug. (We'll define a simple function to extract the bug ID from the URL return value.) >>> def get_bug_id_from_url(url): ... return int(url.split("/")[-1]) >>> params = dict( ... product='firefox', summary='the summary', comment='the comment') >>> bug_url = filebug_api.filebug(params) ObjectCreatedEvent: >>> print bug_url http://bugs.launchpad.dev/bugs/... >>> from zope.component import getUtility >>> from lp.bugs.interfaces.bug import IBugSet >>> bugset = getUtility(IBugSet) >>> bug = bugset.get(get_bug_id_from_url(bug_url)) >>> print bug.title the summary >>> print bug.description the comment >>> print bug.owner.name name12 >>> firefox_bug = bug.bugtasks[0] >>> print firefox_bug.product.name firefox Reporting a distro bug. >>> params = dict( ... distro='ubuntu', summary='another bug', comment='another comment') >>> bug_url = filebug_api.filebug(params) ObjectCreatedEvent: >>> print bug_url http://bugs.launchpad.dev/bugs/... >>> bug = bugset.get(get_bug_id_from_url(bug_url)) >>> print bug.title another bug >>> print bug.description another comment >>> print bug.owner.name name12 >>> ubuntu_bug = bug.bugtasks[0] >>> print ubuntu_bug.distribution.name ubuntu >>> ubuntu_bug.sourcepackagename is None True Reporting a package bug. >>> params = dict( ... distro='ubuntu', package='evolution', summary='email is cool', ... comment='email is nice', security_related=True, ... subscribers=["no-priv@canonical.com"]) >>> bug_url = filebug_api.filebug(params) ObjectCreatedEvent: >>> print bug_url http://bugs.launchpad.dev/bugs/... >>> login('test@canonical.com') >>> bug = bugset.get(get_bug_id_from_url(bug_url)) >>> print bug.title email is cool >>> print bug.description email is nice >>> bug.security_related True >>> bug.private True >>> sorted(p.name for p in bug.getDirectSubscribers()) [u'name12', u'no-priv', u'ubuntu-team'] >>> bug.getIndirectSubscribers() [] >>> evolution_bug = bug.bugtasks[0] >>> print evolution_bug.distribution.name ubuntu >>> print evolution_bug.sourcepackagename.name evolution Error Handling -------------- Malone's xmlrpc interface provides extensive error handling. The various error conditions it recognizes are: Failing to specify a product or distribution. >>> params = dict() >>> filebug_api.filebug(params) Traceback (most recent call last): ... Fault: Specifying *both* a product and distribution. >>> params = dict(product='firefox', distro='ubuntu') >>> filebug_api.filebug(params) Traceback (most recent call last): ... Fault: Specifying a non-existent product. >>> params = dict(product='nosuchproduct') >>> filebug_api.filebug(params) Traceback (most recent call last): ... Fault: Specifying a non-existent distribution. >>> params = dict(distro='nosuchdistro') >>> filebug_api.filebug(params) Traceback (most recent call last): ... Fault: Specifying a non-existent package. >>> params = dict(distro='ubuntu', package='nosuchpackage') >>> filebug_api.filebug(params) Traceback (most recent call last): ... Fault: Missing summary. >>> params = dict(product='firefox') >>> filebug_api.filebug(params) Traceback (most recent call last): ... Fault: Missing comment. >>> params = dict(product='firefox', summary='the summary') >>> filebug_api.filebug(params) Traceback (most recent call last): ... Fault: Invalid subscriber. >>> params = dict( ... product='firefox', summary='summary', comment='comment', ... subscribers=["foo.bar@canonical.com", "nosuch@subscriber.com"]) >>> filebug_api.filebug(params) Traceback (most recent call last): ... Fault: >>> on_created_listener.unregister() Generating bugtracker authentication tokens ------------------------------------------- Launchpad Bugs also provides an XML-RPC API for generating login tokens for authentication with external bug trackers. >>> from zope.component import getUtility >>> from lp.xmlrpc.interfaces import IPrivateApplication >>> from lp.bugs.interfaces.malone import IPrivateMaloneApplication >>> from lp.services.webapp.testing import verifyObject >>> private_root = getUtility(IPrivateApplication) >>> verifyObject(IPrivateMaloneApplication, ... private_root.bugs) True The API provides a single method, newBugTrackerToken(), which returns the ID of the new LoginToken. >>> from lp.services.verification.interfaces.logintoken import ( ... ILoginTokenSet) >>> from lp.services.webapp.servers import LaunchpadTestRequest >>> from lp.bugs.interfaces.externalbugtracker import ( ... IExternalBugTrackerTokenAPI) >>> from lp.bugs.xmlrpc.bug import ( ... ExternalBugTrackerTokenAPI) >>> bugtracker_token_api = ExternalBugTrackerTokenAPI( ... private_root.bugs, LaunchpadTestRequest()) >>> verifyObject(IExternalBugTrackerTokenAPI, bugtracker_token_api) True >>> token_string = bugtracker_token_api.newBugTrackerToken() >>> token = getUtility(ILoginTokenSet)[token_string] >>> token The LoginToken generated will be of the LoginTokenType BUGTRACKER. >>> print token.tokentype.title Launchpad is authenticating itself with a remote bug tracker. These requests are all handled by the private xml-rpc server. >>> bugtracker_api = xmlrpclib.ServerProxy( ... 'http://xmlrpc-private.launchpad.dev:8087/bugs', ... transport=XMLRPCTestTransport()) >>> token_string = bugtracker_api.newBugTrackerToken() >>> token = getUtility(ILoginTokenSet)[token_string] >>> token >>> print token.tokentype.title Launchpad is authenticating itself with a remote bug tracker.