← Back to team overview

launchpad-reviewers team mailing list archive

Re: [Merge] lp:~cjwatson/launchpad/git-sharing-ui into lp:launchpad

 

Review: Approve code

Could to with a s/branch/Bazaar branch/ throughout, and there's some debugging left over, but otherwise looks good.

Diff comments:

> === modified file 'lib/lp/registry/browser/pillar.py'
> --- lib/lp/registry/browser/pillar.py	2015-02-16 13:01:34 +0000
> +++ lib/lp/registry/browser/pillar.py	2015-04-29 10:34:20 +0000
> @@ -425,6 +425,8 @@
>          cache = IJSONRequestCache(self.request)
>          request = get_current_web_service_request()
>          branch_data = self._build_branch_template_data(self.branches, request)
> +        gitrepository_data = self._build_gitrepository_template_data(
> +            self.gitrepositories, request)
>          bug_data = self._build_bug_template_data(self.bugtasks, request)
>          spec_data = self._build_specification_template_data(
>              self.specifications, request)
> @@ -439,6 +441,7 @@
>          cache.objects['pillar'] = pillar_data
>          cache.objects['bugs'] = bug_data
>          cache.objects['branches'] = branch_data
> +        cache.objects['gitrepositories'] = gitrepository_data
>          cache.objects['specifications'] = spec_data
>  
>      def _loadSharedArtifacts(self):
> @@ -476,6 +479,17 @@
>                  information_type=branch.information_type.title))
>          return branch_data
>  
> +    def _build_gitrepository_template_data(self, repositories, request):
> +        repository_data = []
> +        for repository in repositories:
> +            repository_data.append(dict(
> +                self_link=absoluteURL(repository, request),
> +                web_link=canonical_url(repository, path_only_if_possible=True),
> +                repository_name=repository.unique_name,
> +                repository_id=repository.id,
> +                information_type=repository.information_type.title))
> +        return repository_data
> +
>      def _build_bug_template_data(self, bugtasks, request):
>          bug_data = []
>          for bugtask in bugtasks:
> 
> === modified file 'lib/lp/registry/browser/tests/test_pillar_sharing.py'
> --- lib/lp/registry/browser/tests/test_pillar_sharing.py	2013-06-20 05:50:00 +0000
> +++ lib/lp/registry/browser/tests/test_pillar_sharing.py	2015-04-29 10:34:20 +0000
> @@ -1,4 +1,4 @@
> -# Copyright 2012-2013 Canonical Ltd. This software is licensed under the
> +# Copyright 2012-2015 Canonical Ltd. This software is licensed under the
>  # GNU Affero General Public License version 3 (see the file LICENSE).
>  
>  """Test views that manage sharing."""
> @@ -21,6 +21,7 @@
>  
>  from lp.app.enums import InformationType
>  from lp.app.interfaces.services import IService
> +from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
>  from lp.registry.enums import (
>      BranchSharingPolicy,
>      BugSharingPolicy,
> @@ -29,11 +30,13 @@
>  from lp.registry.model.pillar import PillarPerson
>  from lp.services.config import config
>  from lp.services.database.interfaces import IStore
> +from lp.services.features.testing import FeatureFixture
>  from lp.services.webapp.interfaces import StormRangeFactoryError
>  from lp.services.webapp.publisher import canonical_url
>  from lp.testing import (
>      login_person,
>      logout,
> +    normalize_whitespace,
>      person_logged_in,
>      StormStatementRecorder,
>      TestCaseWithFactory,
> @@ -55,6 +58,7 @@
>  
>      def setUp(self):
>          super(SharingBaseTestCase, self).setUp()
> +        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
>          self.driver = self.factory.makePerson()
>          self.owner = self.factory.makePerson()
>          if self.pillar_type == 'distribution':
> @@ -75,11 +79,13 @@
>          return grantee
>  
>      def makeArtifactGrantee(self, grantee=None, with_bug=True,
> -                            with_branch=False, security=False):
> +                            with_branch=False, with_gitrepository=True,
> +                            security=False):
>          if grantee is None:
>              grantee = self.factory.makePerson()
>  
>          branch = None
> +        gitrepository = None
>          bug = None
>          artifacts = []
>  
> @@ -90,6 +96,13 @@
>              artifacts.append(
>                  self.factory.makeAccessArtifact(concrete=branch))
>  
> +        if with_gitrepository and self.pillar_type == 'product':
> +            gitrepository = self.factory.makeGitRepository(
> +                target=self.pillar, owner=self.pillar.owner,
> +                information_type=InformationType.PRIVATESECURITY)
> +            artifacts.append(
> +                self.factory.makeAccessArtifact(concrete=gitrepository))
> +
>          if with_bug:
>              if security:
>                  owner = self.factory.makePerson()
> @@ -120,7 +133,7 @@
>      """Test the pillar sharing details view."""
>  
>      def getPillarPerson(self, person=None, security=False):
> -        person = self.makeArtifactGrantee(person, True, True, security)
> +        person = self.makeArtifactGrantee(person, True, True, True, security)
>          return PillarPerson(self.pillar, person)
>  
>      def test_view_filters_security_wisely(self):
> @@ -160,8 +173,8 @@
>              pillarperson.pillar.name, pillarperson.person.name)
>          browser = self.getUserBrowser(user=self.owner, url=url)
>          self.assertIn(
> -            'There are no shared bugs, branches, or blueprints.',
> -            browser.contents)
> +            'There are no shared bugs, branches, Git repositories, or '
> +            'blueprints.', normalize_whitespace(browser.contents))
>  
>      def test_init_works(self):
>          # The view works with a feature flag.
> @@ -203,19 +216,28 @@
>                  'web_link': canonical_url(branch, path_only_if_possible=True),
>                  'self_link': absoluteURL(branch, request),
>              }, cache.objects.get('branches')[0])
> +            gitrepository = list(view.gitrepositories)[0]
> +            self.assertEqual({
> +                'repository_id': gitrepository.id,
> +                'repository_name': gitrepository.unique_name,
> +                'information_type': gitrepository.information_type.title,
> +                'web_link': canonical_url(
> +                    gitrepository, path_only_if_possible=True),
> +                'self_link': absoluteURL(gitrepository, request),
> +            }, cache.objects.get('gitrepositories')[0])
>  
>      def test_view_query_count(self):
>          # Test that the view bulk loads artifacts.
>          person = self.factory.makePerson()
>          for x in range(0, 15):
> -            self.makeArtifactGrantee(person, True, True, False)
> +            self.makeArtifactGrantee(person, True, True, True, False)
>          pillarperson = PillarPerson(self.pillar, person)
>  
>          # Invalidate the Storm cache and check the query count.
>          IStore(self.pillar).invalidate()
>          with StormStatementRecorder() as recorder:
>              create_initialized_view(pillarperson, '+index')
> -        self.assertThat(recorder, HasQueryCount(LessThan(13)))
> +        self.assertThat(recorder, HasQueryCount(LessThan(14)))
>  
>  
>  class TestProductSharingDetailsView(
> 
> === modified file 'lib/lp/registry/enums.py'
> --- lib/lp/registry/enums.py	2014-04-28 00:18:58 +0000
> +++ lib/lp/registry/enums.py	2015-04-29 10:34:20 +0000
> @@ -1,4 +1,4 @@
> -# Copyright 2010-2013 Canonical Ltd.  This software is licensed under the
> +# Copyright 2010-2015 Canonical Ltd.  This software is licensed under the
>  # GNU Affero General Public License version 3 (see the file LICENSE).
>  
>  """Enums for the Registry app."""
> @@ -56,44 +56,46 @@
>      PUBLIC = DBItem(1, """
>          Public
>  
> -        Branches are public unless they contain sensitive security
> -        information.
> +        Branches and Git repositories are public unless they contain

"Bazaar branches"?

> +        sensitive security information.
>          """)
>  
>      PUBLIC_OR_PROPRIETARY = DBItem(2, """
>          Public, can be proprietary
>  
> -        New branches are public, but can be made proprietary later.
> +        New branches and Git repositories are public, but can be made
> +        proprietary later.
>          """)
>  
>      PROPRIETARY_OR_PUBLIC = DBItem(3, """
>          Proprietary, can be public
>  
> -        New branches are proprietary, but can be made public later. Only
> -        people who can see the project's proprietary information can create
> -        new branches.
> +        New branches and Git repositories are proprietary, but can be made
> +        public later. Only people who can see the project's proprietary
> +        information can create new branches or Git repositories.
>          """)
>  
>      PROPRIETARY = DBItem(4, """
>          Proprietary
>  
> -        Branches are always proprietary. Only people who can see the
> -        project's proprietary information can create new branches.
> +        Branches and Git repositories are always proprietary. Only people
> +        who can see the project's proprietary information can create new
> +        branches or Git repositories.
>          """)
>  
>      EMBARGOED_OR_PROPRIETARY = DBItem(5, """
>          Embargoed, can be proprietary
>  
> -        New branches are embargoed, but can be made proprietary later. Only
> -        people who can see the project's proprietary information can create
> -        new branches.
> +        New branches and Git repositories are embargoed, but can be made
> +        proprietary later. Only people who can see the project's proprietary
> +        information can create new branches or Git repositories.
>          """)
>  
>      FORBIDDEN = DBItem(6, """
>          Forbidden
>  
> -        No new branches may be created, but existing branches may still be
> -        updated.
> +        No new branches or Git repositories may be created, but existing
> +        branches and Git repositories may still be updated.
>          """)
>  
>  
> 
> === modified file 'lib/lp/registry/javascript/sharing/sharingdetails.js'
> --- lib/lp/registry/javascript/sharing/sharingdetails.js	2013-04-09 05:05:39 +0000
> +++ lib/lp/registry/javascript/sharing/sharingdetails.js	2015-04-29 10:34:20 +0000
> @@ -1,4 +1,4 @@
> -/* Copyright 2012 Canonical Ltd.  This software is licensed under the
> +/* Copyright 2012-2015 Canonical Ltd.  This software is licensed under the
>   * GNU Affero General Public License version 3 (see the file LICENSE).
>   *
>   * Sharing details widget
> @@ -74,6 +74,34 @@
>          ].join(' ');
>      },
>  
> +    _gitrepository_details_row_template: function() {
> +        return [
> +        '<tr id="shared-gitrepository-{{ repository_id }}">',
> +        '    <td>',
> +        '        <span class="sortkey">sorttable_branchsortkey</span>',
> +        '        <a href="{{web_link}}">',
> +        '            {{repository_name}}',
> +        '        </a>',
> +        '    </td>',
> +        '    <td class="action-icons nowrap">',
> +        '    <span id="remove-gitrepository-{{repository_id}}">',
> +        '    <a class="sprite remove action-icon" href="#"',
> +        '        title="Unshare Git repository {{repository_name}} with ' +
> +            '{{displayname}}"',
> +        '        data-self_link="{{self_link}}" ' +
> +            'data-name="{{repository_name}}"',
> +        '        data-type="gitrepository">Remove</a>',
> +        '    </span>',
> +        '    </td>',
> +        '   <td>',
> +        '   <span class="information_type">',
> +        '  {{information_type}}',
> +        '   </span>',
> +        '   </td>',
> +        '</tr>'
> +        ].join(' ');
> +    },
> +
>      /**
>       * If the artifact with id exists in the model, return it.
>       * @param artifact_id
> @@ -129,6 +157,9 @@
>          '{{#branches}}',
>          '{{> branch}}',
>          '{{/branches}}',
> +        '{{#gitrepositories}}',
> +        '{{> gitrepository}}',
> +        '{{/gitrepositories}}',
>          '{{#specifications}}',
>          '{{> spec}}',
>          '{{/specifications}}',
> @@ -151,7 +182,7 @@
>      },
>  
>      // Delete the specified grantees from the table.
> -    delete_artifacts: function(bugs, branches, specifications,
> +    delete_artifacts: function(bugs, branches, gitrepositories, specifications,
>                                 all_rows_deleted) {
>          var deleted_row_selectors = [];
>          var details_table_body = this.get('details_table_body');
> @@ -169,6 +200,14 @@
>                  deleted_row_selectors.push(selector);
>              }
>          });
> +        Y.Array.each(gitrepositories, function(gitrepository) {
> +            var selector = 'tr[id=shared-gitrepository-' +
> +                gitrepository.repository_id + ']';
> +            var table_row = details_table_body.one(selector);
> +            if (Y.Lang.isValue(table_row)) {
> +                deleted_row_selectors.push(selector);
> +            }
> +        });
>          Y.Array.each(specifications, function(spec) {
>              var selector = 'tr[id=shared-spec-' + spec.id + ']';
>              var table_row = details_table_body.one(selector);
> @@ -191,7 +230,9 @@
>                  details_table_body
>                      .appendChild('<tr></tr>')
>                      .appendChild('<td colspan="3"></td>')
> -                    .setContent("There are no shared bugs, branches, or blueprints.");
> +                    .setContent(
> +                        "There are no shared bugs, branches, " +
> +                        "Git repositories, or blueprints.");
>              }
>          };
>          var anim_duration = this.get('anim_duration');
> @@ -228,6 +269,10 @@
>              this._branch_details_row_template());
>  
>          this.set(
> +            'gitrepository_details_row_template',
> +            this._gitrepository_details_row_template());
> +
> +        this.set(
>              'spec_details_row_template',
>              this._spec_details_row_template());
>  
> @@ -240,15 +285,17 @@
>      renderUI: function() {
>          // Load the data
>          var branches = this.get('branches');
> +        var gitrepositories = this.get('gitrepositories');
>          var bugs = this.get('bugs');
>          var specs = this.get('specifications');
>  
>          if (bugs.length === 0 && branches.length === 0 &&
> -            specs.length === 0 ) {
> +            gitrepositories.length === 0 && specs.length === 0 ) {
>              return;
>          }
>          var partials = {
>              branch: this.get('branch_details_row_template'),
> +            gitrepository: this.get('gitrepository_details_row_template'),
>              bug: this.get('bug_details_row_template'),
>              spec: this.get('spec_details_row_template')
>          };
> @@ -257,11 +304,13 @@
>              template,
>              {
>                  branches: branches,
> +                gitrepositories: gitrepositories,
>                  bugs: bugs,
>                  specifications: specs,
>                  displayname: this.get('person_name')
>              },
>              partials);
> +        Y.log("cjw html: " + html);

Can probably be dropped now.

>  
>          var details_table_body = this.get('details_table_body');
>          var table_body_node = Y.Node.create(html);
> @@ -294,12 +343,15 @@
>          // been removed.
>          var existing_bugs = this.get('bugs');
>          var existing_branches = this.get('branches');
> +        var existing_gitrepositories = this.get('gitrepositories');
>          var existing_specifications = this.get('specifications');
>          var model_bugs = LP.cache.bugs;
>          var model_branches = LP.cache.branches;
> +        var model_gitrepositories = LP.cache.gitrepositories;
>          var model_specifications = LP.cache.specifications;
>          var deleted_bugs = [];
>          var deleted_branches = [];
> +        var deleted_gitrepositories = [];
>          var deleted_specifications = [];
>  
>          var self = this;
> @@ -319,6 +371,15 @@
>                  deleted_branches.push(branch);
>              }
>          });
> +        Y.Array.each(existing_gitrepositories, function(gitrepository) {
> +            var model_gitrepository =
> +                self._get_artifact_from_model(
> +                    gitrepository.repository_id, 'repository_id',
> +                    model_gitrepositories);
> +            if (!Y.Lang.isValue(model_gitrepository)) {
> +                deleted_gitrepositories.push(gitrepository);
> +            }
> +        });
>  
>          Y.Array.each(existing_specifications, function(spec) {
>              var model_specification = self._get_artifact_from_model(
> @@ -329,14 +390,19 @@
>          });
>  
>          if (deleted_bugs.length > 0 || deleted_branches.length > 0 ||
> +            deleted_gitrepositories.length > 0 ||
>              deleted_specifications.length > 0) {
>              this.delete_artifacts(
> -                deleted_bugs, deleted_branches, deleted_specifications,
> -                model_bugs.length === 0 && model_branches.length === 0 && deleted_specifications.length === 0);
> +                deleted_bugs, deleted_branches, deleted_gitrepositories,
> +                deleted_specifications,
> +                model_bugs.length === 0 && model_branches.length === 0 &&
> +                model_gitrepositories.length === 0 &&
> +                deleted_specifications.length === 0);
>          }
>  
>          this.set('bugs', model_bugs);
>          this.set('branches', model_branches);
> +        this.set('gitrepositories', model_gitrepositories);
>          this.set('specifications', model_specifications);
>  
>          Y.lp.app.sorttable.SortTable.registerSortKeyFunction(
> @@ -360,6 +426,10 @@
>                  value: null
>              },
>  
> +            gitrepository_details_row_template: {
> +                value: null
> +            },
> +
>              spec_details_row_template: {
>                  value: null
>              },
> @@ -371,6 +441,13 @@
>                  setter: clone_data
>              },
>  
> +            gitrepositories: {
> +                value: [],
> +                // We clone the data passed in so external modifications do not
> +                // interfere.
> +                setter: clone_data
> +            },
> +
>              bugs: {
>                  value: [],
>                  // We clone the data passed in so external modifications do not
> 
> === modified file 'lib/lp/registry/javascript/sharing/sharingdetailsview.js'
> --- lib/lp/registry/javascript/sharing/sharingdetailsview.js	2012-12-02 03:01:23 +0000
> +++ lib/lp/registry/javascript/sharing/sharingdetailsview.js	2015-04-29 10:34:20 +0000
> @@ -1,4 +1,4 @@
> -/* Copyright 2012 Canonical Ltd.  This software is licensed under the
> +/* Copyright 2012-2015 Canonical Ltd.  This software is licensed under the
>   * GNU Affero General Public License version 3 (see the file LICENSE).
>   *
>   * Disclosure infrastructure.
> @@ -31,6 +31,7 @@
>          var details_table = new ns.SharingDetailsTable({
>              bugs: LP.cache.bugs,
>              branches: LP.cache.branches,
> +            gitrepositories: LP.cache.gitrepositories,
>              person_name: LP.cache.grantee.displayname,
>              specifications: LP.cache.specifications,
>              write_enabled: true
> @@ -75,6 +76,15 @@
>                      }
>                  });
>                  break;
> +            case 'gitrepository':
> +                Y.Array.some(LP.cache.gitrepositories,
> +                             function(gitrepository) {
> +                    if (gitrepository.self_link === artifact_uri) {
> +                        artifact_id = gitrepository.repository_id;
> +                        return true;
> +                    }
> +                });
> +                break;
>              case 'spec':
>                  Y.Array.some(LP.cache.specifications, function(spec) {
>                      if (spec.self_link === artifact_uri) {
> @@ -181,6 +191,14 @@
>                  return true;
>              }
>          });
> +        var gitrepository_data = LP.cache.gitrepositories;
> +        Y.Array.some(gitrepository_data, function(gitrepository, index) {
> +            if (gitrepository.self_link === artifact_uri) {
> +                gitrepository_data.splice(index, 1);
> +                self.syncUI();
> +                return true;
> +            }
> +        });
>          var spec_data = LP.cache.specifications;
>          Y.Array.some(spec_data, function(spec, index) {
>              if (spec.self_link === artifact_uri) {
> @@ -203,6 +221,7 @@
>          var error_handler = this._error_handler(artifact_uri, artifact_type);
>          var bugs = [];
>          var branches = [];
> +        var gitrepositories = [];
>          var specifications = [];
>          switch (artifact_type) {
>              case 'bug':
> @@ -211,6 +230,9 @@
>              case 'branch':
>                  branches = [artifact_uri];
>                  break;
> +            case 'gitrepository':
> +                gitrepositories = [artifact_uri];
> +                break;
>              case 'spec':
>                  specifications = [artifact_uri];
>                  break;
> @@ -233,6 +255,7 @@
>                  grantee: LP.cache.grantee.self_link,
>                  bugs: bugs,
>                  branches: branches,
> +                gitrepositories: gitrepositories,
>                  specifications: specifications
>              }
>          };
> 
> === modified file 'lib/lp/registry/javascript/sharing/tests/test_sharingdetails.js'
> --- lib/lp/registry/javascript/sharing/tests/test_sharingdetails.js	2013-03-20 03:41:40 +0000
> +++ lib/lp/registry/javascript/sharing/tests/test_sharingdetails.js	2015-04-29 10:34:20 +0000
> @@ -1,4 +1,4 @@
> -/* Copyright 2012 Canonical Ltd.  This software is licensed under the
> +/* Copyright 2012-2015 Canonical Ltd.  This software is licensed under the
>   * GNU Affero General Public License version 3 (see the file LICENSE). */
>  YUI.add('lp.registry.sharing.sharingdetails.test', function(Y) {
>  
> @@ -35,6 +35,15 @@
>                              branch_name:'lp:~someone/+junk/somebranch',
>                              information_type: 'Private'
>                          }
> +                    ],
> +                    gitrepositories: [
> +                        {
> +                            self_link: 'api/devel/~someone/+git/somerepo',
> +                            web_link: '/~someone/+git/somerepo',
> +                            repository_id: '2',
> +                            repository_name: '~someone/+git/somerepo',
> +                            information_type: 'Private'
> +                        }
>                      ]
>                  }
>              };
> @@ -61,6 +70,7 @@
>                  person_name: 'Fred',
>                  bugs: window.LP.cache.bugs,
>                  branches: window.LP.cache.branches,
> +                gitrepositories: window.LP.cache.gitrepositories,
>                  write_enabled: true
>              }, overrides);
>              window.LP.cache.grantee_data = config.grantees;
> @@ -124,6 +134,24 @@
>                  "sorttable_branchsortkey", this._strip(sortkey));
>          },
>  
> +        // Test that Git repositories are correctly rendered.
> +        test_render_gitrepositories: function () {
> +            this.details_widget = this._create_Widget();
> +            this.details_widget.render();
> +            var row = Y.one(
> +                '#sharing-table-body tr#shared-gitrepository-2');
> +            var web_link = row.one('a');
> +            var expected = "~someone/+git/somerepo";
> +            var actual_text = web_link.get('text');
> +            Assert.areEqual(expected, this._strip(actual_text));
> +            var info_type = row.one('.information_type');
> +            var info_text = info_type.get('text');
> +            Assert.areEqual('Private', this._strip(info_text));
> +            var sortkey = row.one('.sortkey').get('text');
> +            Assert.areEqual(
> +                "sorttable_branchsortkey", this._strip(sortkey));
> +        },
> +
>          // Test that bugs are correctly rendered.
>          test_render_bugs: function () {
>              this.details_widget = this._create_Widget();
> @@ -189,6 +217,9 @@
>              var branch_row_selector =
>                  '#sharing-table-body tr[id=shared-branch-2]';
>              Y.Assert.isNotNull(Y.one(branch_row_selector));
> +            var gitrepository_row_selector =
> +                '#sharing-table-body tr[id=shared-gitrepository-2]';
> +            Y.Assert.isNotNull(Y.one(gitrepository_row_selector));
>              // The sorting data is initialised.
>              Y.Assert.isTrue(sorttable_init_called);
>              sorttable_ns.SortTable.init = orig_init;
> @@ -223,6 +254,34 @@
>              Y.Assert.isTrue(event_fired);
>          },
>  
> +        // When the Git repository revoke link is clicked, the correct event
> +        // is published.
> +        test_gitrepository_revoke_click: function() {
> +            this.details_widget = this._create_Widget();
> +            this.details_widget.render();
> +            var event_fired = false;
> +            this.details_widget.subscribe(
> +                sharing_details.SharingDetailsTable.REMOVE_GRANT,
> +                function(e) {
> +                    var delete_link = e.details[0];
> +                    var artifact_uri = e.details[1];
> +                    var artifact_name = e.details[2];
> +                    var artifact_type = e.details[3];
> +                    Y.Assert.areEqual(
> +                        'api/devel/~someone/+git/somerepo',
> +                        artifact_uri);
> +                    Y.Assert.areEqual('~someone/+git/somerepo', artifact_name);
> +                    Y.Assert.areEqual('gitrepository', artifact_type);
> +                    Y.Assert.areEqual(delete_link_to_click, delete_link);
> +                    event_fired = true;
> +                }
> +            );
> +            var delete_link_to_click =
> +                Y.one('#sharing-table-body span[id=remove-gitrepository-2] a');
> +            delete_link_to_click.simulate('click');
> +            Y.Assert.isTrue(event_fired);
> +        },
> +
>          // The delete_artifacts call removes the specified bugs from the table.
>          test_delete_bugs: function() {
>              this.details_widget = this._create_Widget();
> @@ -230,7 +289,7 @@
>              var row_selector = '#sharing-table-body tr[id=shared-bug-2]';
>              Y.Assert.isNotNull(Y.one(row_selector));
>              this.details_widget.delete_artifacts(
> -                [window.LP.cache.bugs[0]], [], false);
> +                [window.LP.cache.bugs[0]], [], [], false);
>              Y.Assert.isNull(Y.one(row_selector));
>          },
>  
> @@ -242,7 +301,20 @@
>              var row_selector = '#sharing-table-body tr[id=shared-branch-2]';
>              Y.Assert.isNotNull(Y.one(row_selector));
>              this.details_widget.delete_artifacts(
> -                [], [window.LP.cache.branches[0]], false);
> +                [], [window.LP.cache.branches[0]], [], false);
> +            Y.Assert.isNull(Y.one(row_selector));
> +        },
> +
> +        // The delete_artifacts call removes the specified Git repositories
> +        // from the table.
> +        test_delete_gitrepositories: function() {
> +            this.details_widget = this._create_Widget();
> +            this.details_widget.render();
> +            var row_selector =
> +                '#sharing-table-body tr[id=shared-gitrepository-2]';
> +            Y.Assert.isNotNull(Y.one(row_selector));
> +            this.details_widget.delete_artifacts(
> +                [], [], [window.LP.cache.gitrepositories[0]], false);
>              Y.Assert.isNull(Y.one(row_selector));
>          },
>  
> @@ -252,9 +324,12 @@
>              this.details_widget.render();
>              this.details_widget.delete_artifacts(
>                  [window.LP.cache.bugs[0]],
> -                [window.LP.cache.branches[0]], true);
> +                [window.LP.cache.branches[0]],
> +                [window.LP.cache.gitrepositories[0]],
> +                true);
>              Y.Assert.areEqual(
> -                'There are no shared bugs, branches, or blueprints.',
> +                'There are no shared bugs, branches, Git repositories, or ' +
> +                'blueprints.',
>                  Y.one('#sharing-table-body tr').get('text'));
>          },
>  
> @@ -286,6 +361,23 @@
>              };
>              this.details_widget.display_error(2, 'branch', 'An error occurred');
>              Y.Assert.isTrue(success);
> +        },
> +
> +        test_display_error_gitrepository: function() {
> +            // A Git repository operation error is displayed correctly.
> +            this.details_widget = this._create_Widget();
> +            this.details_widget.render();
> +            var row_gitrepository = Y.one(
> +                '#sharing-table-body tr[id=shared-gitrepository-2]');
> +            var success = false;
> +            Y.lp.app.errors.display_error = function(flash_node, msg) {
> +                Y.Assert.areEqual(row_gitrepository, flash_node);
> +                Y.Assert.areEqual('An error occurred', msg);
> +                success = true;
> +            };
> +            this.details_widget.display_error(
> +                2, 'gitrepository', 'An error occurred');
> +            Y.Assert.isTrue(success);
>          }
>      }));
>  
> 
> === modified file 'lib/lp/registry/javascript/sharing/tests/test_sharingdetailsview.js'
> --- lib/lp/registry/javascript/sharing/tests/test_sharingdetailsview.js	2013-03-20 03:41:40 +0000
> +++ lib/lp/registry/javascript/sharing/tests/test_sharingdetailsview.js	2015-04-29 10:34:20 +0000
> @@ -1,4 +1,4 @@
> -/* Copyright 2012 Canonical Ltd.  This software is licensed under the
> +/* Copyright 2012-2015 Canonical Ltd.  This software is licensed under the
>   * GNU Affero General Public License version 3 (see the file LICENSE). */
>  
>  YUI.add('lp.registry.sharing.sharingdetailsview.test', function (Y) {
> @@ -31,12 +31,21 @@
>                              branch_name:'lp:~someone/+junk/somebranch'
>                          }
>                      ],
> +                    gitrepositories: [
> +                        {
> +                            self_link: 'api/devel/~someone/+git/somerepo',
> +                            web_link: '/~someone/+git/somerepo',
> +                            repository_id: '2',
> +                            repository_name: '~someone/+git/somerepo'
> +                        }
> +                    ],
>                      specifications: [
>                          {
>                              id: 2,
>                              information_type: "Proprietary",
>                              name: "big-project",
> -                            self_link: "api/devel/obsolete-junk/+spec/big-project",
> +                            self_link:
> +                                "api/devel/obsolete-junk/+spec/big-project",
>                              web_link: "/obsolete-junk/+spec/big-project"
>                          }
>                      ],
> @@ -109,7 +118,7 @@
>              Y.Assert.isTrue(confirmRemove_called);
>          },
>  
> -        // Clicking a bug remove link calls the confirm_grant_removal
> +        // Clicking a branch remove link calls the confirm_grant_removal
>          // method with the correct parameters.
>          test_remove_branch_grant_click: function() {
>              this.view = this._create_Widget();
> @@ -132,6 +141,28 @@
>              Y.Assert.isTrue(confirmRemove_called);
>          },
>  
> +        // Clicking a Git repository remove link calls the
> +        // confirm_grant_removal method with the correct parameters.
> +        test_remove_gitrepository_grant_click: function() {
> +            this.view = this._create_Widget();
> +            this.view.render();
> +            var confirmRemove_called = false;
> +            this.view.confirm_grant_removal = function(
> +                    delete_link, artifact_uri, artifact_name, artifact_type) {
> +                Y.Assert.areEqual(
> +                    'api/devel/~someone/+git/somerepo', artifact_uri);
> +                Y.Assert.areEqual('~someone/+git/somerepo', artifact_name);
> +                Y.Assert.areEqual('gitrepository', artifact_type);
> +                Y.Assert.areEqual(delete_link_to_click, delete_link);
> +                confirmRemove_called = true;
> +
> +            };
> +            var delete_link_to_click =
> +                Y.one('#sharing-table-body span[id=remove-gitrepository-2] a');
> +            delete_link_to_click.simulate('click');
> +            Y.Assert.isTrue(confirmRemove_called);
> +        },
> +
>          // Clicking a spec remove link calls the confirm_grant_removal
>          // method with the correct parameters.
>          test_remove_spec_grant_click: function() {
> @@ -273,6 +304,47 @@
>              Y.Assert.isTrue(remove_grant_success_called);
>          },
>  
> +        // The perform_remove_grant method makes the expected XHR calls when a
> +        // Git repository grant remove link is clicked.
> +        test_perform_remove_gitrepository_grant: function() {
> +            var mockio = new Y.lp.testing.mockio.MockIo();
> +            var lp_client = new Y.lp.client.Launchpad({
> +                io_provider: mockio
> +            });
> +            this.view = this._create_Widget({
> +                lp_client: lp_client
> +            });
> +            this.view.render();
> +            var remove_grant_success_called = false;
> +            var self = this;
> +            this.view.remove_grant_success = function(artifact_uri) {
> +                Y.Assert.areEqual(
> +                    'api/devel/~someone/+git/somerepo', artifact_uri);
> +                remove_grant_success_called = true;
> +            };
> +            var delete_link =
> +                Y.one('#sharing-table-body span[id=remove-gitrepository-2] a');
> +            this.view.perform_remove_grant(
> +                delete_link, 'api/devel/~someone/+git/somerepo',
> +                'gitrepository');
> +            Y.Assert.areEqual(
> +                '/api/devel/+services/sharing',
> +                mockio.last_request.url);
> +            var expected_qs = '';
> +            expected_qs = Y.lp.client.append_qs(
> +                expected_qs, 'ws.op', 'revokeAccessGrants');
> +            expected_qs = Y.lp.client.append_qs(
> +                expected_qs, 'pillar', '/pillar');
> +            expected_qs = Y.lp.client.append_qs(
> +                expected_qs, 'grantee', '~fred');
> +            expected_qs = Y.lp.client.append_qs(
> +                expected_qs, 'gitrepositories',
> +                'api/devel/~someone/+git/somerepo');
> +            Y.Assert.areEqual(expected_qs, mockio.last_request.config.data);
> +            mockio.last_request.successJSON({});
> +            Y.Assert.isTrue(remove_grant_success_called);
> +        },
> +
>          // The remove bug grant callback updates the model and syncs the UI.
>          test_remove_bug_grant_success: function() {
>              this.view = this._create_Widget({anim_duration: 0});
> @@ -306,6 +378,23 @@
>              });
>          },
>  
> +        // The remove Git repository grant callback updates the model and
> +        // syncs the UI.
> +        test_remove_gitrepository_grant_success: function() {
> +            this.view = this._create_Widget({anim_duration: 0});
> +            this.view.render();
> +            var syncUI_called = false;
> +            this.view.syncUI = function() {
> +                syncUI_called = true;
> +            };
> +            this.view.remove_grant_success('api/devel/~someone/+git/somerepo');
> +            Y.Assert.isTrue(syncUI_called);
> +            Y.Array.each(window.LP.cache.gitrepositories,
> +                function(gitrepository) {
> +                    Y.Assert.areNotEqual(2, gitrepository.repository_id);
> +            });
> +        },
> +
>          // The remove specification grant callback updates the model and syncs
>          // the UI.
>          test_remove_spec_grant_success: function() {
> @@ -326,7 +415,7 @@
>  
>          // XHR calls display errors correctly.
>          _assert_error_displayed_on_failure: function(
> -                bug_or_branch, invoke_operation) {
> +                artifact, invoke_operation) {
>              var mockio = new Y.lp.testing.mockio.MockIo();
>              var lp_client = new Y.lp.client.Launchpad({
>                  io_provider: mockio
> @@ -340,7 +429,7 @@
>              grantee_table.display_error = function(
>                      artifact_id, artifact_type, error_msg) {
>                  Y.Assert.areEqual(2, artifact_id);
> -                Y.Assert.areEqual(bug_or_branch, artifact_type);
> +                Y.Assert.areEqual(artifact, artifact_type);
>                  Y.Assert.areEqual(
>                      'Server error, please contact an administrator.',
>                      error_msg);
> @@ -378,6 +467,20 @@
>              this._assert_error_displayed_on_failure('branch', invoke_remove);
>          },
>  
> +        // The perform_remove_grant method handles errors correctly with Git
> +        // repositories.
> +        test_perform_remove_gitrepository_error: function() {
> +            var invoke_remove = function(view) {
> +            var delete_link =
> +                Y.one('#sharing-table-body span[id=remove-gitrepository-2] a');
> +                view.perform_remove_grant(
> +                    delete_link, 'api/devel/~someone/+git/somerepo',
> +                    'gitrepository');
> +            };
> +            this._assert_error_displayed_on_failure(
> +                'gitrepository', invoke_remove);
> +        },
> +
>          // The perform_remove_grant method handles errors correctly with
>          // specifications.
>          test_perform_remove_spec_error: function() {
> 
> === modified file 'lib/lp/registry/templates/pillar-sharing-details.pt'
> --- lib/lp/registry/templates/pillar-sharing-details.pt	2012-12-03 12:49:35 +0000
> +++ lib/lp/registry/templates/pillar-sharing-details.pt	2015-04-29 10:34:20 +0000
> @@ -29,14 +29,15 @@
>        <p>
>        <tal:bugs replace="view/shared_bugs_count">0</tal:bugs> bugs,
>        <tal:branches replace="view/shared_branches_count">0</tal:branches> branches,
> +      <tal:gitrepositories replace="view/shared_gitrepositories_count">0</tal:gitrepositories> Git repositories,
>        and  <tal:specifications
>        replace="view/shared_specifications_count">0</tal:specifications>
>        blueprints shared with <tal:name replace="view/person/displayname">
>        grantee</tal:name>.<br />
>  
>        <tal:is-team condition="view/person/is_team">
> -        <tal:members>3</tal:members> team members can view these bugs and
> -        branches.
> +        <tal:members>3</tal:members> team members can view these bugs,
> +        branches, Git repositories, and blueprints.
>        </tal:is-team>
>        </p>
>      </div>
> @@ -48,7 +49,7 @@
>        <thead>
>          <tr>
>            <th colspan="2" width="">
> -            Subscribed Bug Report, Branch, or Blueprint
> +            Subscribed Bug Report, Branch, Git Repository, or Blueprint
>            </th>
>            <th>
>              Information Type
> @@ -58,7 +59,8 @@
>        <tbody id="sharing-table-body">
>            <tr>
>                <td colspan="3">
> -                  There are no shared bugs, branches, or blueprints.
> +                  There are no shared bugs, branches, Git repositories, or
> +                  blueprints.
>                </td>
>            </tr>
>        </tbody>
> 
> === modified file 'lib/lp/registry/templates/pillar-sharing.pt'
> --- lib/lp/registry/templates/pillar-sharing.pt	2012-12-03 18:42:09 +0000
> +++ lib/lp/registry/templates/pillar-sharing.pt	2015-04-29 10:34:20 +0000
> @@ -102,7 +102,8 @@
>        <p tal:condition="python: info_type == 'Public'"
>           tal:content="string:
>          Everyone can see ${context/displayname}'s public information. You can
> -        choose who can see the private bugs, branches and blueprints.">
> +        choose who can see the private bugs, branches, Git repositories, and
> +        blueprints.">
>       </p>
>       <p tal:condition="python: info_type != 'Public'"
>         tal:content="string: This project has no public information.">
> 


-- 
https://code.launchpad.net/~cjwatson/launchpad/git-sharing-ui/+merge/257735
Your team Launchpad code reviewers is subscribed to branch lp:launchpad.


References