← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~pappacena/launchpad:snap-pillar-ui into launchpad:master

 

Thiago F. Pappacena has proposed merging ~pappacena/launchpad:snap-pillar-ui into launchpad:master with ~pappacena/launchpad:snap-pillar as a prerequisite.

Commit message:
UI to associate a project pillar to Snaps on +admin view

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/397529
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad:snap-pillar-ui into launchpad:master.
diff --git a/lib/lp/snappy/browser/snap.py b/lib/lp/snappy/browser/snap.py
index 3b06a89..eeaecfc 100644
--- a/lib/lp/snappy/browser/snap.py
+++ b/lib/lp/snappy/browser/snap.py
@@ -1,4 +1,4 @@
-# Copyright 2015-2020 Canonical Ltd.  This software is licensed under the
+# Copyright 2015-2021 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Snap views."""
@@ -344,6 +344,7 @@ class ISnapEditSchema(Interface):
         'owner',
         'name',
         'private',
+        'project',
         'require_virtualized',
         'allow_internet',
         'build_source_tarball',
@@ -613,6 +614,7 @@ class BaseSnapEditView(LaunchpadEditFormView, SnapAuthorizeMixin):
     def validate(self, data):
         super(BaseSnapEditView, self).validate(data)
         if data.get('private', self.context.private) is False:
+            # These are the requirements for public snaps.
             if 'private' in data or 'owner' in data:
                 owner = data.get('owner', self.context.owner)
                 if owner is not None and owner.private:
@@ -631,6 +633,14 @@ class BaseSnapEditView(LaunchpadEditFormView, SnapAuthorizeMixin):
                     self.setFieldError(
                         'private' if 'private' in data else 'git_ref',
                         'A public snap cannot have a private repository.')
+        else:
+            # These are the requirements for private snaps.
+            project = data.get('project', self.context.project)
+            private = data.get('private', self.context.private)
+            if private and project is None:
+                msg = ('Private Snap recipes should be associated '
+                       'with a project.')
+                self.setFieldError('project', msg)
 
     def _needStoreReauth(self, data):
         """Does this change require reauthorizing to the store?"""
@@ -696,7 +706,8 @@ class SnapAdminView(BaseSnapEditView):
 
     page_title = 'Administer'
 
-    field_names = ['private', 'require_virtualized', 'allow_internet']
+    field_names = [
+        'project', 'private', 'require_virtualized', 'allow_internet']
 
     def validate(self, data):
         super(SnapAdminView, self).validate(data)
diff --git a/lib/lp/snappy/browser/tests/test_snap.py b/lib/lp/snappy/browser/tests/test_snap.py
index 3e48162..d5f0c7d 100644
--- a/lib/lp/snappy/browser/tests/test_snap.py
+++ b/lib/lp/snappy/browser/tests/test_snap.py
@@ -661,6 +661,7 @@ class TestSnapAdminView(BaseTestSnapView):
 
         browser = self.getViewBrowser(snap, user=commercial_admin)
         browser.getLink("Administer snap package").click()
+        browser.getControl(name='field.project').value = "my-project"
         browser.getControl("Require virtualized builders").selected = False
         browser.getControl("Private").selected = True
         browser.getControl("Allow external network access").selected = False
@@ -671,6 +672,21 @@ class TestSnapAdminView(BaseTestSnapView):
         self.assertTrue(snap.private)
         self.assertFalse(snap.allow_internet)
 
+    def test_admin_snap_private_without_project(self):
+        # Cannot make snap private if it doesn't have a project associated.
+        login_person(self.person)
+        snap = self.factory.makeSnap(registrant=self.person)
+        commercial_admin = self.factory.makePerson(
+            member_of=[getUtility(ILaunchpadCelebrities).commercial_admin])
+        browser = self.getViewBrowser(snap, user=commercial_admin)
+        browser.getLink("Administer snap package").click()
+        browser.getControl(name='field.project').value = ''
+        browser.getControl("Private").selected = True
+        browser.getControl("Update snap package").click()
+        self.assertEqual(
+            'Private Snap recipes should be associated with a project.',
+            extract_text(find_tags_by_class(browser.contents, "message")[1]))
+
     def test_admin_snap_privacy_mismatch(self):
         # Cannot make snap public if it still contains private information.
         login_person(self.person)
diff --git a/lib/lp/snappy/interfaces/snap.py b/lib/lp/snappy/interfaces/snap.py
index e17e224..8aa065c 100644
--- a/lib/lp/snappy/interfaces/snap.py
+++ b/lib/lp/snappy/interfaces/snap.py
@@ -670,7 +670,7 @@ class ISnapEditableAttributes(IHasOwner):
         description=_("The owner of this snap package.")))
 
     project = ReferenceChoice(
-        title=_('The project that this Snap is associated with.'),
+        title=_('The project that this Snap is associated with'),
         schema=IProduct, vocabulary='Product',
         required=False, readonly=False)
 
@@ -884,7 +884,7 @@ class ISnapSet(Interface):
             "git_repository", "git_repository_url", "git_path", "git_ref",
             "auto_build", "auto_build_archive", "auto_build_pocket",
             "private", "store_upload", "store_series", "store_name",
-            "store_channels"])
+            "store_channels", "project"])
     @operation_for_version("devel")
     def new(registrant, owner, distro_series, name, description=None,
             branch=None, git_repository=None, git_repository_url=None,

Follow ups