← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~pappacena/launchpad:oci-api-create-project into launchpad:master

 

Thiago F. Pappacena has proposed merging ~pappacena/launchpad:oci-api-create-project into launchpad:master.

Commit message:
API operation to create a new OCIProject for a Distribution.

The feature is only enabled if we turn on the 'oci.project.create.enabled' feature flag.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/381189
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad:oci-api-create-project into launchpad:master.
diff --git a/lib/lp/_schema_circular_imports.py b/lib/lp/_schema_circular_imports.py
index 46247fc..a8d119e 100644
--- a/lib/lp/_schema_circular_imports.py
+++ b/lib/lp/_schema_circular_imports.py
@@ -329,6 +329,8 @@ patch_entry_return_type(ISourcePackagePublic, 'getBranch', IBranch)
 patch_plain_parameter_type(ISourcePackageEdit, 'setBranch', 'branch', IBranch)
 patch_reference_property(ISourcePackage, 'distribution', IDistribution)
 
+patch_entry_return_type(IDistribution, 'newOCIProject', IOCIProject)
+
 # IPerson
 patch_entry_return_type(IPerson, 'createRecipe', ISourcePackageRecipe)
 patch_list_parameter_type(IPerson, 'createRecipe', 'distroseries',
diff --git a/lib/lp/registry/interfaces/distribution.py b/lib/lp/registry/interfaces/distribution.py
index a7106f6..886b02e 100644
--- a/lib/lp/registry/interfaces/distribution.py
+++ b/lib/lp/registry/interfaces/distribution.py
@@ -22,6 +22,7 @@ from lazr.restful.declarations import (
     collection_default_content,
     export_as_webservice_collection,
     export_as_webservice_entry,
+    export_factory_operation,
     export_operation_as,
     export_read_operation,
     exported,
@@ -655,6 +656,40 @@ class IDistributionPublic(
     def userCanEdit(user):
         """Can the user edit this distribution?"""
 
+    # XXX: pappacena 2020-04-25: This method is sit on IDistributionPublic
+    # for now, until we workout the specific permission for creating OCI
+    # Projects.
+    @call_with(registrant=REQUEST_USER)
+    @operation_parameters(
+        ociprojectname=Text(
+            title=_("The OCI project name."),
+            description=_("The name that groups a set of OCI projects "
+                          "together.")),
+        description=Text(
+            title=_("Description for this OCI project."),
+            description=_("A short description of this OCI project.")),
+        bug_reporting_guidelines=Text(
+            title=_("The guidelines to report a bug."),
+            description=_("What is the guideline to report a bug to this "
+                          "OCI Project?")),
+        bug_reported_acknowledgement=Text(
+            title=_("Acknowledgement text for a bug reported."),
+            description=_("Acknowledgement text for a bug reported in this "
+                          "OCI Project.")),
+        bugfiling_duplicate_search=Bool(
+            title=_("Show bug search before allowing to open a bug?"),
+            description=_("To avoid duplicate bugs, show to the user a bug "
+                          "search before allowing them to create new bugs?"))
+    )
+    # Interface is actually IOCIProject. Fixed at _schema_circular_imports
+    @export_factory_operation(Interface, [])
+    @operation_for_version("devel")
+    def newOCIProject(
+        registrant, ociprojectname, description=None,
+        bug_reporting_guidelines=None, bug_reported_acknowledgement=None,
+        bugfiling_duplicate_search=False):
+        """Create an `IOCIProject` for this distro."""
+
 
 class IDistribution(
     IDistributionEditRestricted, IDistributionPublic, IHasBugSupervisor,
diff --git a/lib/lp/registry/model/distribution.py b/lib/lp/registry/model/distribution.py
index f78908b..d82a375 100644
--- a/lib/lp/registry/model/distribution.py
+++ b/lib/lp/registry/model/distribution.py
@@ -35,6 +35,7 @@ from storm.info import ClassAlias
 from storm.store import Store
 from zope.component import getUtility
 from zope.interface import implementer
+from zope.security.interfaces import Unauthorized
 
 from lp.answers.enums import QUESTION_STATUS_DEFAULT_SEARCH
 from lp.answers.model.faq import (
@@ -130,6 +131,7 @@ from lp.registry.model.milestone import (
     HasMilestonesMixin,
     Milestone,
     )
+from lp.registry.model.ociproject import OCI_PROJECT_ALLOW_CREATE
 from lp.registry.model.oopsreferences import referenced_oops
 from lp.registry.model.pillar import HasAliasMixin
 from lp.registry.model.sourcepackagename import SourcePackageName
@@ -147,6 +149,7 @@ from lp.services.database.stormexpr import (
     fti_search,
     rank_by_fti,
     )
+from lp.services.features import getFeatureFlag
 from lp.services.helpers import shortlist
 from lp.services.propertycache import (
     cachedproperty,
@@ -1450,6 +1453,20 @@ class Distribution(SQLBase, BugTargetBase, MakesAnnouncements,
                 return True
         return False
 
+    def newOCIProject(self, registrant, ociprojectname, description=None,
+            bug_reporting_guidelines=None, bug_reported_acknowledgement=None,
+            bugfiling_duplicate_search=False):
+        """Create an `IOCIProject` for this distro."""
+        if not getFeatureFlag(OCI_PROJECT_ALLOW_CREATE):
+            raise Unauthorized("Creating new OCI projects is not allowed.")
+        return getUtility(IOCIProjectSet).new(
+            pillar=self,
+            registrant=registrant, ociprojectname=ociprojectname,
+            description=description,
+            bug_reporting_guidelines=bug_reporting_guidelines,
+            bug_reported_acknowledgement=bug_reported_acknowledgement,
+            bugfiling_duplicate_search=bugfiling_duplicate_search)
+
 
 @implementer(IDistributionSet)
 class DistributionSet:
diff --git a/lib/lp/registry/model/ociproject.py b/lib/lp/registry/model/ociproject.py
index 0b1390a..55b3ce3 100644
--- a/lib/lp/registry/model/ociproject.py
+++ b/lib/lp/registry/model/ociproject.py
@@ -7,11 +7,13 @@ from __future__ import absolute_import, print_function, unicode_literals
 
 __metaclass__ = type
 __all__ = [
+    'OCI_PROJECT_ALLOW_CREATE',
     'OCIProject',
     'OCIProjectSet',
     ]
 
 import pytz
+from six import string_types
 from storm.locals import (
     Bool,
     DateTime,
@@ -44,6 +46,9 @@ from lp.services.database.interfaces import (
 from lp.services.database.stormbase import StormBase
 
 
+OCI_PROJECT_ALLOW_CREATE = 'oci.project.create.enabled'
+
+
 def oci_project_modified(oci_project, event):
     """Update the date_last_modified property when an OCIProject is modified.
 
@@ -140,6 +145,9 @@ class OCIProjectSet:
             bug_reported_acknowledgement=None,
             bugfiling_duplicate_search=False):
         """See `IOCIProjectSet`."""
+        if isinstance(ociprojectname, string_types):
+            ociprojectname = getUtility(IOCIProjectNameSet).getOrCreateByName(
+                ociprojectname)
         store = IMasterStore(OCIProject)
         target = OCIProject()
         target.date_created = date_created
@@ -158,6 +166,7 @@ class OCIProjectSet:
         target.ociprojectname = ociprojectname
         target.description = description
         target.bug_reporting_guidelines = bug_reporting_guidelines
+        target.bug_reported_acknowledgement = bug_reported_acknowledgement
         target.enable_bugfiling_duplicate_search = bugfiling_duplicate_search
         store.add(target)
         return target
diff --git a/lib/lp/registry/tests/test_ociproject.py b/lib/lp/registry/tests/test_ociproject.py
index af1ca26..7e291ce 100644
--- a/lib/lp/registry/tests/test_ociproject.py
+++ b/lib/lp/registry/tests/test_ociproject.py
@@ -10,6 +10,7 @@ __metaclass__ = type
 import json
 
 from six import string_types
+from storm.store import Store
 from testtools.matchers import (
     ContainsDict,
     Equals,
@@ -24,6 +25,12 @@ from lp.registry.interfaces.ociproject import (
     IOCIProjectSet,
     )
 from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries
+from lp.registry.model.ociproject import (
+    OCI_PROJECT_ALLOW_CREATE,
+    OCIProject,
+    )
+from lp.services.features.testing import FeatureFixture
+from lp.services.macaroons.testing import MatchesStructure
 from lp.services.webapp.interfaces import OAuthPermission
 from lp.testing import (
     admin_logged_in,
@@ -142,6 +149,7 @@ class TestOCIProjectWebservice(TestCaseWithFactory):
         self.webservice = webservice_for_person(
             self.person, permission=OAuthPermission.WRITE_PUBLIC,
             default_api_version="devel")
+        self.useFixture(FeatureFixture({OCI_PROJECT_ALLOW_CREATE: 'on'}))
 
     def getAbsoluteURL(self, target):
         """Get the webservice absolute URL of the given object or relative
@@ -215,3 +223,51 @@ class TestOCIProjectWebservice(TestCaseWithFactory):
 
         ws_project = self.load_from_api(url)
         self.assertEqual("old description", ws_project['description'])
+
+    def test_create_oci_project(self):
+        with person_logged_in(self.person):
+            distro = removeSecurityProxy(self.factory.makeDistribution(
+                owner=self.person))
+            url = api_url(distro)
+
+        obj = {
+            "ociprojectname": "someprojectname",
+            "description": "My OCI project",
+            "bug_reporting_guidelines": "Bug reporting guide",
+            "bug_reported_acknowledgement": "Bug reporting ack",
+            "bugfiling_duplicate_search": True,
+        }
+        resp = self.webservice.named_post(url, "newOCIProject", **obj)
+        self.assertEqual(201, resp.status, resp.body)
+
+        store = Store.of(distro)
+        result_set = [i for i in store.find(OCIProject)]
+
+        self.assertEqual(1, len(result_set))
+        self.assertThat(result_set[0], MatchesStructure(
+            ociprojectname=MatchesStructure(
+                name=Equals(obj["ociprojectname"])),
+            description=Equals(obj["description"]),
+            bug_reporting_guidelines=Equals(obj["bug_reporting_guidelines"]),
+            bug_reported_acknowledgement=Equals(
+                obj["bug_reported_acknowledgement"]),
+            enable_bugfiling_duplicate_search=Equals(
+                obj["bugfiling_duplicate_search"])
+            ))
+
+    def test_api_create_oci_project_is_disabled_by_feature_flag(self):
+        self.useFixture(FeatureFixture({OCI_PROJECT_ALLOW_CREATE: ''}))
+        with person_logged_in(self.person):
+            distro = removeSecurityProxy(self.factory.makeDistribution(
+                owner=self.person))
+            url = api_url(distro)
+
+        obj = {
+            "ociprojectname": "someprojectname",
+            "description": "My OCI project",
+            "bug_reporting_guidelines": "Bug reporting guide",
+            "bug_reported_acknowledgement": "Bug reporting ack",
+            "bugfiling_duplicate_search": True,
+        }
+        resp = self.webservice.named_post(url, "newOCIProject", **obj)
+        self.assertEqual(401, resp.status, resp.body)

Follow ups