launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #32804
Re: [Merge] ~enriqueesanchz/launchpad:add-external-package-series into launchpad:master
Looks good, added a couple of questions
Diff comments:
> diff --git a/lib/lp/bugs/browser/bugtask.py b/lib/lp/bugs/browser/bugtask.py
> index 081825d..91e3314 100644
> --- a/lib/lp/bugs/browser/bugtask.py
> +++ b/lib/lp/bugs/browser/bugtask.py
> @@ -343,23 +346,18 @@ class BugTargetTraversalMixin:
> # anonymous user is presented with a login screen at the correct URL,
> # rather than making it look as though this task was "not found",
> # because it was filtered out by privacy-aware code.
> - is_external_package = IExternalPackage.providedBy(context)
> +
> + # Check if it uses +external url
> + is_external = IExternalURL.providedBy(context)
> +
> for bugtask in bug.bugtasks:
> target = bugtask.target
>
> - if is_external_package:
> - # +external url lacks necessary data, so we only match
> - # distribution and sourcepackagename, then using +bugktask we
> - # can jump to the right one
> - if (
> - IExternalPackage.providedBy(target)
> - and target.sourcepackagename == context.sourcepackagename
> - and target.distribution == context.distribution
> - ):
> + if is_external:
> + if context.isMatching(target):
Nice, this looks way cleaner
> + # Security proxy the object on the way out
> return getUtility(IBugTaskSet).get(bugtask.id)
> -
> elif target == context:
> - # Security proxy the object on the way out
> return getUtility(IBugTaskSet).get(bugtask.id)
>
> # If we've come this far, there's no task for the requested context.
> @@ -1883,22 +1881,45 @@ def bugtask_sort_key(bugtask):
> None,
> None,
> )
> + # Version should only be compared to items with same object type
> elif ISourcePackage.providedBy(bugtask.target):
> key = (
> bugtask.target.sourcepackagename.name,
> bugtask.target.distribution.displayname,
> + None,
Why do we need to added these None here, but not in the `IDistributionSourcePackage` section above?
> + None,
> + Version(bugtask.target.distroseries.version),
> + None,
> + )
> + elif IExternalPackageSeries.providedBy(bugtask.target):
> + key = (
> + bugtask.target.sourcepackagename.name,
> + bugtask.target.distribution.displayname,
> + bugtask.target.packagetype,
> + bugtask.target.channel,
> Version(bugtask.target.distroseries.version),
> None,
> None,
> None,
> )
> elif IProduct.providedBy(bugtask.target):
> - key = (None, None, None, bugtask.target.displayname, None, None)
> + key = (
> + None,
> + None,
> + None,
> + None,
> + None,
> + bugtask.target.displayname,
> + None,
> + None,
> + )
> elif IProductSeries.providedBy(bugtask.target):
> key = (
> None,
> None,
> None,
> + None,
> + None,
> bugtask.target.product.displayname,
> bugtask.target.name,
> None,
> diff --git a/lib/lp/bugs/browser/tests/test_bugtask_navigation.py b/lib/lp/bugs/browser/tests/test_bugtask_navigation.py
> index e30f0ce..bb41781 100644
> --- a/lib/lp/bugs/browser/tests/test_bugtask_navigation.py
> +++ b/lib/lp/bugs/browser/tests/test_bugtask_navigation.py
> @@ -162,15 +187,50 @@ class TestBugTaskTraversal(TestCaseWithFactory):
> ),
> )
>
> + def test_traversal_to_default_external_package_series_bugtask(self):
> + # Test that a traversing to a bug with an external package series
> + # as default bugtask redirects to the bug's default bugtask using
> + # +bugtask/id.
> + bug = self.factory.makeBug(target=self.ep)
> + bug_url = canonical_url(bug, rootsite="bugs")
> +
> + # We need to create a bugtask in the distribution before creating it in
> + # the distroseries
> + eps_bugtask = self.factory.makeBugTask(bug=bug, target=self.eps)
> +
> + # Deleting the distribution bugtask to change the default one
> + login_person(bug.owner)
> + bug.default_bugtask.delete()
> + self.assertEqual(eps_bugtask, bug.default_bugtask)
> +
> + obj, view, request = test_traverse(bug_url)
> + view()
What does this `view()` do here? Is it relevant for the test?
> + naked_view = removeSecurityProxy(view)
> + self.assertEqual(303, request.response.getStatus())
> + self.assertEqual(
> + naked_view.target,
> + canonical_url(bug.default_bugtask, rootsite="bugs"),
> + )
> + self.assertEqual(
> + removeSecurityProxy(view).target,
> + "http://bugs.launchpad.test/%s/%s/+external/%s/+bug/%d/+bugtask/%s"
> + % (
> + bug.default_bugtask.target.distribution.name,
> + bug.default_bugtask.distroseries.name,
> + bug.default_bugtask.target.name,
> + bug.default_bugtask.bug.id,
> + bug.default_bugtask.id,
> + ),
> + )
> +
> def test_traversal_to_default_external_package_bugtask_on_api(self):
> # Traversing to a bug with an external package as default task
> # redirects to the +bugtask/id also in the API.
> - ep = self.factory.makeExternalPackage()
> - bug = self.factory.makeBug(target=ep)
> + bug = self.factory.makeBug(target=self.ep)
> obj, view, request = test_traverse(
> "http://api.launchpad.test/1.0/%s/+bug/%d"
> % (
> - removeSecurityProxy(ep).distribution.name,
> + removeSecurityProxy(self.ep).distribution.name,
> bug.default_bugtask.bug.id,
> )
> )
> diff --git a/lib/lp/registry/browser/externalpackageseries.py b/lib/lp/registry/browser/externalpackageseries.py
> new file mode 100644
> index 0000000..9e8f1a6
> --- /dev/null
> +++ b/lib/lp/registry/browser/externalpackageseries.py
> @@ -0,0 +1,70 @@
> +# Copyright 2009-2025 Canonical Ltd. This software is licensed under the
nit: chnage this to Copyright 2025 Canonical (no need for a range, and add the up-to-date year)
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +__all__ = [
> + "ExternalPackageSeriesBreadcrumb",
> + "ExternalPackageSeriesNavigation",
> + "ExternalPackageSeriesFacets",
> +]
> +
> +
> +from zope.interface import implementer
> +
> +from lp.app.interfaces.headings import IHeadingBreadcrumb
> +from lp.bugs.browser.bugtask import BugTargetTraversalMixin
> +from lp.bugs.browser.structuralsubscription import (
> + StructuralSubscriptionTargetTraversalMixin,
> +)
> +from lp.registry.interfaces.externalpackageseries import IExternalPackageSeries
> +from lp.services.webapp import (
> + Navigation,
> + StandardLaunchpadFacets,
> + canonical_url,
> + redirection,
> + stepto,
> +)
> +from lp.services.webapp.breadcrumb import Breadcrumb
> +from lp.services.webapp.interfaces import IMultiFacetedBreadcrumb
> +
> +
> +@implementer(IHeadingBreadcrumb, IMultiFacetedBreadcrumb)
> +class ExternalPackageSeriesBreadcrumb(Breadcrumb):
> + """Builds a breadcrumb for an `IExternalPackageSeries`."""
> +
> + rootsite = "bugs"
> +
> + @property
> + def text(self):
> + return "%s external package in %s" % (
> + self.context.sourcepackagename.name,
> + self.context.distroseries.named_version,
> + )
> +
> +
> +class ExternalPackageSeriesFacets(StandardLaunchpadFacets):
> + usedfor = IExternalPackageSeries
> + enable_only = [
> + "bugs",
> + ]
> +
> +
> +class ExternalPackageSeriesNavigation(
> + Navigation,
> + BugTargetTraversalMixin,
> + StructuralSubscriptionTargetTraversalMixin,
> +):
> + usedfor = IExternalPackageSeries
> +
> + @redirection("+editbugcontact")
> + def redirect_editbugcontact(self):
> + return "+subscribe"
> +
> + @stepto("+filebug")
> + def filebug(self):
> + """Redirect to the IExternalPackage +filebug page."""
> + external_package = self.context.distribution_sourcepackage
> +
> + redirection_url = canonical_url(external_package, view_name="+filebug")
> + if self.request.form.get("no-redirect") is not None:
> + redirection_url += "?no-redirect"
> + return self.redirectSubTree(redirection_url, status=303)
> diff --git a/lib/lp/registry/interfaces/externalpackageseries.py b/lib/lp/registry/interfaces/externalpackageseries.py
> new file mode 100644
> index 0000000..8aef040
> --- /dev/null
> +++ b/lib/lp/registry/interfaces/externalpackageseries.py
> @@ -0,0 +1,90 @@
> +# Copyright 2009, 2025 Canonical Ltd. This software is licensed under the
nit: same about copyright (and a few others below)
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +"""External Package Series interface."""
> +
> +__all__ = [
> + "IExternalPackageSeries",
> +]
> +
> +from lazr.restful.declarations import exported, exported_as_webservice_entry
> +from lazr.restful.fields import Reference
> +from zope.interface import Attribute
> +from zope.schema import TextLine
> +
> +from lp import _
> +from lp.app.interfaces.launchpad import IHeadingContext
> +from lp.bugs.interfaces.bugtarget import IBugTarget, IHasOfficialBugTags
> +from lp.registry.interfaces.distribution import IDistribution
> +from lp.registry.interfaces.distroseries import IDistroSeries
> +from lp.registry.interfaces.externalpackage import IExternalURL
> +from lp.registry.interfaces.role import IHasDrivers
> +
> +
> +@exported_as_webservice_entry(as_of="beta")
> +class IExternalPackageSeriesView(
> + IHeadingContext,
> + IBugTarget,
> + IHasOfficialBugTags,
> + IHasDrivers,
> + IExternalURL,
> +):
> + """`IExternalPackageSeries` attributes that require launchpad.View."""
> +
> + packagetype = Attribute("The package type")
> +
> + channel = Attribute("The package channel")
> +
> + display_channel = TextLine(title=_("Display channel name"), readonly=True)
> +
> + distribution = exported(
> + Reference(IDistribution, title=_("The distribution."))
> + )
> + distroseries = exported(
> + Reference(IDistroSeries, title=_("The distroseries."))
> + )
> + sourcepackagename = Attribute("The source package name.")
> +
> + distribution_sourcepackage = Attribute(
> + "The IExternalPackage for this external package series."
> + )
> +
> + name = exported(
> + TextLine(title=_("The source package name as text"), readonly=True)
> + )
> + display_name = exported(
> + TextLine(title=_("Display name for this package."), readonly=True)
> + )
> + displayname = Attribute("Display name (deprecated)")
> + title = exported(
> + TextLine(title=_("Title for this package."), readonly=True)
> + )
> +
> + drivers = Attribute("The drivers for the distroseries.")
> +
> + def isMatching(other):
> + """See `IExternalURL`."""
> +
> + def __eq__(other):
> + """IExternalPackageSeries comparison method.
> +
> + ExternalPackageSeries compare equal only if their fields compare equal.
> + """
> +
> + def __ne__(other):
> + """IExternalPackageSeries comparison method.
> +
> + External packages compare not equal if either of their
> + fields compare not equal.
> + """
> +
> +
> +@exported_as_webservice_entry(as_of="beta")
> +class IExternalPackageSeries(
> + IExternalPackageSeriesView,
> +):
> + """Represents an ExternalPackage in a distroseries.
> +
> + Create IExternalPackageSeries by invoking
> + `IDistroSeries.getExternalPackageSeries()`.
> + """
--
https://code.launchpad.net/~enriqueesanchz/launchpad/+git/launchpad/+merge/489721
Your team Launchpad code reviewers is requested to review the proposed merge of ~enriqueesanchz/launchpad:add-external-package-series into launchpad:master.
References