launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #02859
[Merge] lp:~abentley/launchpad/export-translation-api into lp:launchpad
Aaron Bentley has proposed merging lp:~abentley/launchpad/export-translation-api into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~abentley/launchpad/export-translation-api/+merge/52257
= Summary ==
Permit translation configuration via JavaScript
== Proposed fix ==
Export the following over the web service:
IProduct.translation_permission
IProduct.translation_group
IProduct.translations_usage
IProductSeries.translations_autoimport_mode
ISourcePackage.setPackaging
ISourcePackage.deletePackaging
ITranslationGroup.name
ITranslationGroup.title
ITranslationGroupSet.getByName
== Pre-implementation notes ==
Discussed with henninge
== Implementation details ==
Implemented WebServiceTestCase to simplify writing test cases. Made ws_object's URL handling somewhat cleaner by specifying the correct root
URL.
Removed obsolete comment by Mark-- SQLObject is now irrelevant, and we do not discourage accessing objects by id.
Drive-by cleanup of xx-change-assignee.txt. At first, I was just fixing the spelling of "tries". That'll learn me :-)
Had to implement ITranslationGroupSet._get to allow it to be defined as a collection. Provided __getitem_ as getByName to match other root collections. Renamed parameter to 'key' to match the model.
Implemented SourcePackage.deletePackaging instead of allowing setPackaging to take None, because lazr.restful doesn't support non-default None, and for symmetry with ProductSeries.setPackaging (which cannot work this way, since there may be multiple Packagings).
== Tests ==
bin/test -t xx-change-assignee.txt -t TestWebService
== Demo and Q/A ==
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/registry/tests/test_productseries.py
lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt
lib/lp/translations/interfaces/translationpolicy.py
lib/lp/translations/model/translationgroup.py
lib/lp/registry/interfaces/sourcepackage.py
lib/lp/registry/tests/test_sourcepackage.py
lib/lp/translations/interfaces/translationgroup.py
lib/lp/testing/__init__.py
lib/lp/app/interfaces/launchpad.py
lib/lp/translations/interfaces/webservice.py
lib/lp/registry/interfaces/productseries.py
lib/lp/registry/model/sourcepackage.py
lib/lp/translations/tests/test_translationgroup.py
lib/lp/registry/tests/test_product.py
./lib/lp/translations/interfaces/translationgroup.py
45: 'TranslationPermission' imported but unused
^^^ This is imported so that other clients can import it from here.
./lib/lp/testing/__init__.py
135: 'anonymous_logged_in' imported but unused
135: 'with_anonymous_login' imported but unused
154: 'launchpadlib_for' imported but unused
154: 'launchpadlib_credentials_for' imported but unused
135: 'with_person_logged_in' imported but unused
135: 'person_logged_in' imported but unused
154: 'oauth_access_token_for' imported but unused
135: 'login_celebrity' imported but unused
135: 'with_celebrity_logged_in' imported but unused
153: 'test_tales' imported but unused
135: 'celebrity_logged_in' imported but unused
135: 'run_with_login' imported but unused
135: 'is_logged_in' imported but unused
135: 'login_team' imported but unused
135: 'login_person' imported but unused
135: 'login_as' imported but unused
^^^ These are imported so that other clients can import them from here.
./lib/lp/translations/interfaces/webservice.py
26: 'IPOFile' imported but unused
28: 'ITranslationGroupSet' imported but unused
28: 'ITranslationGroup' imported but unused
23: 'IHasTranslationImports' imported but unused
32: 'ITranslationImportQueue' imported but unused
32: 'ITranslationImportQueueEntry' imported but unused
27: 'IPOTemplate' imported but unused
^^^ These are imported so that lazr.restful can disover them here.
--
https://code.launchpad.net/~abentley/launchpad/export-translation-api/+merge/52257
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~abentley/launchpad/export-translation-api into lp:launchpad.
=== modified file 'lib/lp/app/interfaces/launchpad.py'
--- lib/lp/app/interfaces/launchpad.py 2010-08-25 00:01:57 +0000
+++ lib/lp/app/interfaces/launchpad.py 2011-03-04 21:43:00 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Interfaces for the Launchpad application.
@@ -13,6 +13,7 @@
'IServiceUsage',
]
+from lazr.restful.declarations import exported
from zope.interface import Interface
from zope.schema import (
Bool,
@@ -45,11 +46,11 @@
description=_("Where does this pillar host code?"),
default=ServiceUsage.UNKNOWN,
vocabulary=ServiceUsage)
- translations_usage = Choice(
+ translations_usage = exported(Choice(
title=_('Type of service for translations application'),
description=_("Where does this pillar do translations?"),
default=ServiceUsage.UNKNOWN,
- vocabulary=ServiceUsage)
+ vocabulary=ServiceUsage))
bug_tracking_usage = Choice(
title=_('Type of service for tracking bugs'),
description=_("Where does this pillar track bugs?"),
=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt 2010-10-09 16:36:22 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt 2011-03-04 21:43:00 +0000
@@ -1,4 +1,5 @@
-= Changing bug assignment =
+Changing bug assignment
+=======================
A bug is unassigned by choosing the "Assigned to" -> "Nobody" option.
@@ -17,11 +18,13 @@
>>> admin_browser.getControl("Save Changes", index=0).click()
- >>> admin_browser.getControl(name="firefox.assignee.option", index=0).value
+ >>> admin_browser.getControl(
+ ... name="firefox.assignee.option", index=0).value
['firefox.assignee.assign_to_nobody']
-== Bug assignment to non-contributors ==
+Bug assignment to non-contributors
+==================================
When attempting to assign a bug to a user who isn't an established bug
contributor (they have no bugs currently assigned to them) the user is
@@ -88,7 +91,8 @@
None
-== Bug task assignment by regular users ==
+Bug task assignment by regular users
+====================================
Regular users can only set themselves and their teams as assignees if
there is a bug supervisor established for a project.
@@ -138,7 +142,7 @@
>>> user_browser.getControl("Save Changes", index=0).click()
>>> print_errors(user_browser.contents)
-But if he treis to set other persons or teams, he gets an error message.
+But if he tries to set other persons or teams, he gets an error message.
>>> user_browser.open("http://bugs.launchpad.dev/jokosher/+bug/11")
>>> assignee_control = user_browser.getControl(
=== modified file 'lib/lp/registry/interfaces/productseries.py'
--- lib/lp/registry/interfaces/productseries.py 2011-02-24 15:30:54 +0000
+++ lib/lp/registry/interfaces/productseries.py 2011-03-04 21:43:00 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
# pylint: disable-msg=E0211,E0213
@@ -131,9 +131,6 @@
ISpecificationGoal, IHasMilestones, IHasOfficialBugTags,
IHasTranslationImports, IHasTranslationTemplates, IServiceUsage):
"""Public IProductSeries properties."""
- # XXX Mark Shuttleworth 2004-10-14: Would like to get rid of id in
- # interfaces, as soon as SQLobject allows using the object directly
- # instead of using object.id.
id = Int(title=_('ID'))
product = exported(
@@ -237,12 +234,12 @@
description=_("The Bazaar branch for this series. Leave blank "
"if this series is not maintained in Bazaar.")))
- translations_autoimport_mode = Choice(
+ translations_autoimport_mode = exported(Choice(
title=_('Import settings'),
vocabulary=TranslationsBranchImportMode,
required=True,
description=_("Specify which files will be imported from the "
- "source code branch."))
+ "source code branch.")))
potemplate_count = Int(
title=_("The total number of POTemplates in this series."),
=== modified file 'lib/lp/registry/interfaces/sourcepackage.py'
--- lib/lp/registry/interfaces/sourcepackage.py 2010-11-15 16:25:05 +0000
+++ lib/lp/registry/interfaces/sourcepackage.py 2011-03-04 21:43:00 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009, 2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
# pylint: disable-msg=E0211,E0213
@@ -54,6 +54,7 @@
IHasCodeImports,
IHasMergeProposals,
)
+from lp.registry.interfaces.productseries import IProductSeries
from lp.soyuz.interfaces.component import IComponent
from lp.translations.interfaces.hastranslationtemplates import (
IHasTranslationTemplates,
@@ -195,12 +196,19 @@
sourcepackagename compare not equal.
"""
+ @operation_parameters(productseries=Reference(schema=IProductSeries))
+ @call_with(owner=REQUEST_USER)
+ @export_write_operation()
def setPackaging(productseries, owner):
"""Update the existing packaging record, or create a new packaging
record, that links the source package to the given productseries,
and record that it was done by the owner.
"""
+ @export_write_operation()
+ def deletePackaging():
+ """Delete the packaging for this sourcepackage."""
+
def getSuiteSourcePackage(pocket):
"""Return the `ISuiteSourcePackage` for this package in 'pocket'.
=== modified file 'lib/lp/registry/model/sourcepackage.py'
--- lib/lp/registry/model/sourcepackage.py 2011-01-27 22:17:07 +0000
+++ lib/lp/registry/model/sourcepackage.py 2011-03-04 21:43:00 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009, 2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
# pylint: disable-msg=E0611,W0212
@@ -13,12 +13,10 @@
from operator import attrgetter
-from sqlobject.sqlbuilder import SQLConstant
from storm.locals import (
And,
Desc,
Select,
- SQL,
Store,
)
from zope.component import getUtility
@@ -529,23 +527,30 @@
'BugTask.distroseries = %s AND BugTask.sourcepackagename = %s' %
sqlvalues(self.distroseries, self.sourcepackagename))
- def setPackaging(self, productseries, user):
+ def setPackaging(self, productseries, owner):
+ """See `ISourcePackage`."""
target = self.direct_packaging
if target is not None:
# we should update the current packaging
target.productseries = productseries
- target.owner = user
+ target.owner = owner
target.datecreated = UTC_NOW
else:
# ok, we need to create a new one
Packaging(
distroseries=self.distroseries,
sourcepackagename=self.sourcepackagename,
- productseries=productseries, owner=user,
+ productseries=productseries, owner=owner,
packaging=PackagingType.PRIME)
# and make sure this change is immediately available
flush_database_updates()
+ def deletePackaging(self):
+ """See `ISourcePackage`."""
+ if self.direct_packaging is None:
+ return
+ self.direct_packaging.destroySelf()
+
def __hash__(self):
"""See `ISourcePackage`."""
return hash(self.distroseries.id) ^ hash(self.sourcepackagename.id)
=== modified file 'lib/lp/registry/tests/test_product.py'
--- lib/lp/registry/tests/test_product.py 2011-01-06 15:48:25 +0000
+++ lib/lp/registry/tests/test_product.py 2011-03-04 21:43:00 +0000
@@ -21,6 +21,7 @@
DatabaseFunctionalLayer,
LaunchpadFunctionalLayer,
)
+from lp.app.enums import ServiceUsage
from lp.registry.interfaces.product import (
IProduct,
License,
@@ -36,7 +37,9 @@
login,
login_person,
TestCaseWithFactory,
+ WebServiceTestCase,
)
+from lp.translations.enums import TranslationPermission
class TestProduct(TestCaseWithFactory):
@@ -354,5 +357,34 @@
self.product.bug_supervisor.name))
+class TestWebService(WebServiceTestCase):
+
+ def test_translations_usage(self):
+ """The translations_usage field should be writable."""
+ product = self.factory.makeProduct()
+ transaction.commit()
+ ws_product = self.wsObject(product, product.owner)
+ ws_product.translations_usage = ServiceUsage.EXTERNAL.title
+ ws_product.lp_save()
+
+ def test_translationpermission(self):
+ """The translationpermission field should be writable."""
+ product = self.factory.makeProduct()
+ transaction.commit()
+ ws_product = self.wsObject(product, product.owner)
+ ws_product.translationpermission = TranslationPermission.OPEN.title
+ ws_product.lp_save()
+
+ def test_translationgroup(self):
+ """The translationgroup field should be writable."""
+ product = self.factory.makeProduct()
+ group = self.factory.makeTranslationGroup()
+ transaction.commit()
+ ws_product = self.wsObject(product, product.owner)
+ ws_group = self.wsObject(group)
+ ws_product.translationgroup = ws_group
+ ws_product.lp_save()
+
+
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)
=== modified file 'lib/lp/registry/tests/test_productseries.py'
--- lib/lp/registry/tests/test_productseries.py 2011-01-12 18:05:13 +0000
+++ lib/lp/registry/tests/test_productseries.py 2011-03-04 21:43:00 +0000
@@ -1,10 +1,11 @@
-# Copyright 2010 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Tests for ProductSeries and ProductSeriesSet."""
__metaclass__ = type
+import transaction
from zope.component import getUtility
from canonical.launchpad.ftests import login
@@ -19,7 +20,10 @@
IProductSeries,
IProductSeriesSet,
)
-from lp.testing import TestCaseWithFactory
+from lp.testing import (
+ TestCaseWithFactory,
+ WebServiceTestCase,
+ )
from lp.testing.matchers import DoesNotSnapshot
from lp.translations.interfaces.translations import (
TranslationsBranchImportMode,
@@ -318,3 +322,15 @@
self.assertThat(
productseries,
DoesNotSnapshot(skipped, IProductSeries))
+
+
+class TestWebService(WebServiceTestCase):
+
+ def test_translations_autoimport_mode(self):
+ """Autoimport mode can be set over Web Service."""
+ series = self.factory.makeProductSeries()
+ transaction.commit()
+ ws_series = self.wsObject(series, series.owner)
+ mode = TranslationsBranchImportMode.IMPORT_TRANSLATIONS
+ ws_series.translations_autoimport_mode = mode.title
+ ws_series.lp_save()
=== modified file 'lib/lp/registry/tests/test_sourcepackage.py'
--- lib/lp/registry/tests/test_sourcepackage.py 2010-10-26 15:47:24 +0000
+++ lib/lp/registry/tests/test_sourcepackage.py 2011-03-04 21:43:00 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009, 2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Unit tests for ISourcePackage implementations."""
@@ -7,6 +7,7 @@
import unittest
+import transaction
from zope.component import getUtility
from zope.security.interfaces import Unauthorized
from zope.security.proxy import removeSecurityProxy
@@ -31,6 +32,7 @@
from lp.testing import (
person_logged_in,
TestCaseWithFactory,
+ WebServiceTestCase,
)
from lp.testing.views import create_initialized_view
@@ -255,6 +257,39 @@
self.assertEqual(''.join(expected_summary), sp.summary)
+class TestSourcePackageWebService(WebServiceTestCase):
+
+ def test_setPackaging(self):
+ """setPackaging is accessible and works."""
+ sourcepackage = self.factory.makeSourcePackage()
+ self.assertIs(None, sourcepackage.direct_packaging)
+ productseries = self.factory.makeProductSeries()
+ transaction.commit()
+ ws_sourcepackage = self.wsObject(sourcepackage)
+ ws_productseries = self.wsObject(productseries)
+ ws_sourcepackage.setPackaging(productseries=ws_productseries)
+ transaction.commit()
+ self.assertEqual(
+ productseries, sourcepackage.direct_packaging.productseries)
+
+ def test_deletePackaging(self):
+ """Deleting a packaging should work."""
+ packaging = self.factory.makePackagingLink()
+ sourcepackage = packaging.sourcepackage
+ transaction.commit()
+ self.wsObject(sourcepackage).deletePackaging()
+ transaction.commit()
+ self.assertIs(None, sourcepackage.direct_packaging)
+
+ def test_deletePackaging_with_no_packaging(self):
+ """Deleting when there's no packaging should be a no-op."""
+ sourcepackage = self.factory.makeSourcePackage()
+ transaction.commit()
+ self.wsObject(sourcepackage).deletePackaging()
+ transaction.commit()
+ self.assertIs(None, sourcepackage.direct_packaging)
+
+
class TestSourcePackageSecurity(TestCaseWithFactory):
"""Tests for source package branch linking security."""
=== modified file 'lib/lp/testing/__init__.py'
--- lib/lp/testing/__init__.py 2011-02-25 02:08:52 +0000
+++ lib/lp/testing/__init__.py 2011-03-04 21:43:00 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009, 2010 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
# pylint: disable-msg=W0401,C0301,F0401
@@ -785,6 +785,32 @@
return client, obj_url
+class WebServiceTestCase(TestCaseWithFactory):
+ """Test case optimized for testing the web service using launchpadlib."""
+
+ #avoid circular imports
+ from canonical.testing.layers import AppServerLayer
+
+ layer = AppServerLayer
+
+ def setUp(self):
+ super(WebServiceTestCase, self).setUp()
+ self.service = self.factory.makeLaunchpadService()
+
+ def wsObject(self, obj, user=None):
+ """Return the launchpadlib version of the supplied object.
+
+ :param obj: The object to find the launchpadlib equivalent of.
+ :param user: The user to use for accessing the object over
+ lauchpadlib. Defaults to an arbitrary logged-in user.
+ """
+ if user is not None:
+ service = self.factory.makeLaunchpadService(user)
+ else:
+ service = self.service
+ return ws_object(service, obj)
+
+
def quote_jquery_expression(expression):
"""jquery requires meta chars used in literals escaped with \\"""
return re.sub(
@@ -1155,11 +1181,8 @@
:param obj: The object to convert.
:return: A launchpadlib Entry object.
"""
- api_request = WebServiceTestRequest()
- obj_url = canonical_url(obj, request=api_request)
- return launchpad.load(
- obj_url.replace('http://api.launchpad.dev/',
- str(launchpad._root_uri)))
+ api_request = WebServiceTestRequest(SERVER_URL=str(launchpad._root_uri))
+ return launchpad.load(canonical_url(obj, request=api_request))
@contextmanager
=== modified file 'lib/lp/translations/interfaces/translationgroup.py'
--- lib/lp/translations/interfaces/translationgroup.py 2011-02-23 20:26:53 +0000
+++ lib/lp/translations/interfaces/translationgroup.py 2011-03-04 21:43:00 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
# pylint: disable-msg=E0211,E0213
@@ -10,8 +10,19 @@
__all__ = [
'ITranslationGroup',
'ITranslationGroupSet',
+ 'TranslationPermission',
]
+from lazr.restful.declarations import (
+ collection_default_content,
+ exported,
+ export_as_webservice_entry,
+ export_as_webservice_collection,
+ export_read_operation,
+ export_operation_as,
+ operation_parameters,
+ operation_returns_entry,
+ )
from zope.interface import (
Attribute,
Interface,
@@ -37,17 +48,20 @@
class ITranslationGroup(IHasOwner):
"""A TranslationGroup."""
+ export_as_webservice_entry(
+ singular_name='translation_group', plural_name='translation_groups')
+
id = Int(
title=_('Translation Group ID'), required=True, readonly=True,
)
- name = TextLine(
+ name = exported(TextLine(
title=_('Name'), required=True,
description=_("""Keep this name very short, unique, and
descriptive, because it will be used in URLs. Examples:
gnome-translation-project, ubuntu-translators."""),
constraint=name_validator,
- )
- title = Title(
+ ))
+ title = exported(Title(
title=_('Title'), required=True,
description=_("""Title of this Translation Group.
This title is displayed at the top of the Translation Group
@@ -55,7 +69,7 @@
add "translation group" to this title, or it will be shown
double.
"""),
- )
+ ))
summary = Summary(
title=_('Summary'), required=True,
description=_("""A single-paragraph description of the
@@ -148,11 +162,22 @@
class ITranslationGroupSet(Interface):
"""A container for translation groups."""
+ export_as_webservice_collection(ITranslationGroup)
+
title = Attribute('Title')
- def __getitem__(key):
+ @operation_parameters(
+ name=TextLine(title=_("Name of the translation group"),))
+ @operation_returns_entry(ITranslationGroup)
+ @export_operation_as('getByName')
+ @export_read_operation()
+ def __getitem__(name):
"""Get a translation group by name."""
+ @collection_default_content()
+ def _get():
+ """Return a collection of all entries."""
+
def __iter__():
"""Iterate through the translation groups in this set."""
=== modified file 'lib/lp/translations/interfaces/translationpolicy.py'
--- lib/lp/translations/interfaces/translationpolicy.py 2010-11-10 05:04:16 +0000
+++ lib/lp/translations/interfaces/translationpolicy.py 2011-03-04 21:43:00 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Translation access and sharing policy."""
@@ -8,11 +8,18 @@
'ITranslationPolicy',
]
+from lazr.restful.declarations import (
+ exported,
+ )
+from lazr.restful.fields import (
+ ReferenceChoice,
+ )
from zope.interface import Interface
from zope.schema import Choice
from canonical.launchpad import _
from lp.translations.enums import TranslationPermission
+from lp.translations.interfaces.translationgroup import ITranslationGroup
class ITranslationPolicy(Interface):
@@ -32,20 +39,21 @@
user: translation team and translation policy.
"""
- translationgroup = Choice(
+ translationgroup = exported(ReferenceChoice(
title = _("Translation group"),
description = _("The translation group that helps review "
" translations for this project or distribution. The group's "
" role depends on the permissions policy selected below."),
required=False,
- vocabulary='TranslationGroup')
+ vocabulary='TranslationGroup',
+ schema=ITranslationGroup))
- translationpermission = Choice(
+ translationpermission = exported(Choice(
title=_("Translation permissions policy"),
description=_("The policy this project or distribution uses to "
" balance openness and control for their translations."),
required=True,
- vocabulary=TranslationPermission)
+ vocabulary=TranslationPermission))
def getTranslationGroups():
"""List all applicable translation groups.
=== modified file 'lib/lp/translations/interfaces/webservice.py'
--- lib/lp/translations/interfaces/webservice.py 2010-11-09 16:25:22 +0000
+++ lib/lp/translations/interfaces/webservice.py 2011-03-04 21:43:00 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009, 2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
# pylint: disable-msg=W0611
@@ -15,6 +15,7 @@
'IHasTranslationImports',
'IPOFile',
'IPOTemplate',
+ 'ITranslationGroup',
'ITranslationImportQueue',
'ITranslationImportQueueEntry',
]
@@ -24,6 +25,10 @@
)
from lp.translations.interfaces.pofile import IPOFile
from lp.translations.interfaces.potemplate import IPOTemplate
+from lp.translations.interfaces.translationgroup import (
+ ITranslationGroup,
+ ITranslationGroupSet,
+)
from lp.translations.interfaces.translationimportqueue import (
ITranslationImportQueue,
ITranslationImportQueueEntry,
=== modified file 'lib/lp/translations/model/translationgroup.py'
--- lib/lp/translations/model/translationgroup.py 2010-12-02 16:13:51 +0000
+++ lib/lp/translations/model/translationgroup.py 2011-03-04 21:43:00 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
# pylint: disable-msg=E0611,W0212
@@ -35,7 +35,9 @@
LibraryFileAlias,
LibraryFileContent,
)
-from canonical.launchpad.interfaces.lpstorm import ISlaveStore
+from canonical.launchpad.interfaces.lpstorm import (
+ ISlaveStore,
+ )
from lp.app.errors import NotFoundError
from lp.registry.interfaces.person import validate_public_person
from lp.registry.model.person import Person
@@ -178,7 +180,7 @@
Language.id == Translator.languageID,
Person.id == Translator.translatorID)
translator_data = translator_data.order_by(Language.englishname)
- mapper = lambda row:row[slice(0,3)]
+ mapper = lambda row: row[slice(0, 3)]
return DecoratedResultSet(translator_data, mapper)
def fetchProjectsForDisplay(self):
@@ -286,6 +288,9 @@
except SQLObjectNotFound:
raise NotFoundError(name)
+ def _get(self):
+ return self
+
def new(self, name, title, summary, translation_guide_url, owner):
"""See ITranslationGroupSet."""
return TranslationGroup(
=== modified file 'lib/lp/translations/tests/test_translationgroup.py'
--- lib/lp/translations/tests/test_translationgroup.py 2010-10-04 19:50:45 +0000
+++ lib/lp/translations/tests/test_translationgroup.py 2011-03-04 21:43:00 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Unit tests for `TranslationGroup` and related classes."""
@@ -7,6 +7,8 @@
from unittest import TestLoader
+from lazr.restfulclient.errors import Unauthorized
+import transaction
from zope.component import getUtility
from canonical.testing.layers import ZopelessDatabaseLayer
@@ -14,7 +16,10 @@
ITeamMembershipSet,
TeamMembershipStatus,
)
-from lp.testing import TestCaseWithFactory
+from lp.testing import (
+ TestCaseWithFactory,
+ WebServiceTestCase,
+ )
from lp.translations.interfaces.translationgroup import ITranslationGroupSet
@@ -63,5 +68,26 @@
list(getUtility(ITranslationGroupSet).getByPerson(person)))
+class TestWebService(WebServiceTestCase):
+
+ def test_getByName(self):
+ """getByName returns the TranslationGroup for the specified name."""
+ group = self.factory.makeTranslationGroup()
+ transaction.commit()
+ ws_group = self.service.translation_groups.getByName(name=group.name)
+ self.assertEqual(group.name, ws_group.name)
+
+ def test_attrs(self):
+ """TranslationGroup provides the expected attributes."""
+ group = self.factory.makeTranslationGroup()
+ transaction.commit()
+ ws_group = self.wsObject(group)
+ self.assertEqual(group.name, ws_group.name)
+ self.assertEqual(group.title, ws_group.title)
+ ws_group.name = 'foo'
+ e = self.assertRaises(Unauthorized, ws_group.lp_save)
+ self.assertIn("'name', 'launchpad.Edit'", str(e))
+
+
def test_suite():
return TestLoader().loadTestsFromName(__name__)
Follow ups