launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #26715
[Merge] ~pappacena/launchpad:ocirecipe-sharing-lists into launchpad:master
Thiago F. Pappacena has proposed merging ~pappacena/launchpad:ocirecipe-sharing-lists into launchpad:master with ~pappacena/launchpad:ocirecipe-edit-info-type-ui as a prerequisite.
Commit message:
Showing snaps and ocirecipes as shareable artifacts on +sharing pages
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/400059
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad:ocirecipe-sharing-lists into launchpad:master.
diff --git a/lib/lp/registry/browser/pillar.py b/lib/lp/registry/browser/pillar.py
index c5bfd3f..739b56f 100644
--- a/lib/lp/registry/browser/pillar.py
+++ b/lib/lp/registry/browser/pillar.py
@@ -422,6 +422,9 @@ class PillarPersonSharingView(LaunchpadView):
bug_data = self._build_bug_template_data(self.bugtasks, request)
spec_data = self._build_specification_template_data(
self.specifications, request)
+ snap_data = self._build_ocirecipe_template_data(self.snaps, request)
+ ocirecipe_data = self._build_ocirecipe_template_data(
+ self.ocirecipes, request)
grantee_data = {
'displayname': self.person.displayname,
'self_link': absoluteURL(self.person, request)
@@ -435,6 +438,8 @@ class PillarPersonSharingView(LaunchpadView):
cache.objects['branches'] = branch_data
cache.objects['gitrepositories'] = gitrepository_data
cache.objects['specifications'] = spec_data
+ cache.objects['snaps'] = snap_data
+ cache.objects['ocirecipes'] = ocirecipe_data
def _loadSharedArtifacts(self):
# As a concrete can by linked via more than one policy, we use sets to
@@ -504,3 +509,25 @@ class PillarPersonSharingView(LaunchpadView):
bug_importance=importance,
information_type=information_type))
return bug_data
+
+ def _build_ocirecipe_template_data(self, oci_recipes, request):
+ recipe_data = []
+ for recipe in oci_recipes:
+ recipe_data.append(dict(
+ self_link=absoluteURL(recipe, request),
+ web_link=canonical_url(recipe, path_only_if_possible=True),
+ name=recipe.name,
+ id=recipe.id,
+ information_type=recipe.information_type.title))
+ return recipe_data
+
+ def _build_snap_template_data(self, snaps, request):
+ snap_data = []
+ for snap in snaps:
+ snap_data.append(dict(
+ self_link=absoluteURL(snap, request),
+ web_link=canonical_url(snap, path_only_if_possible=True),
+ name=snap.name,
+ id=snap.id,
+ information_type=snap.information_type.title))
+ return snap_data
diff --git a/lib/lp/registry/browser/tests/test_pillar_sharing.py b/lib/lp/registry/browser/tests/test_pillar_sharing.py
index 493b42b..215f946 100644
--- a/lib/lp/registry/browser/tests/test_pillar_sharing.py
+++ b/lib/lp/registry/browser/tests/test_pillar_sharing.py
@@ -172,8 +172,9 @@ class PillarSharingDetailsMixin:
pillarperson.pillar.name, pillarperson.person.name)
browser = self.getUserBrowser(user=self.owner, url=url)
self.assertIn(
- 'There are no shared bugs, Bazaar branches, Git repositories, or '
- 'blueprints.', normalize_whitespace(browser.contents))
+ 'There are no shared bugs, Bazaar branches, Git repositories, '
+ 'Snaps, OCI recipes or blueprints.',
+ normalize_whitespace(browser.contents))
def test_init_works(self):
# The view works with a feature flag.
diff --git a/lib/lp/registry/javascript/sharing/sharingdetails.js b/lib/lp/registry/javascript/sharing/sharingdetails.js
index 0ac4f32..60b56d5 100644
--- a/lib/lp/registry/javascript/sharing/sharingdetails.js
+++ b/lib/lp/registry/javascript/sharing/sharingdetails.js
@@ -1,4 +1,4 @@
-/* Copyright 2012-2015 Canonical Ltd. This software is licensed under the
+/* Copyright 2012-2021 Canonical Ltd. This software is licensed under the
* GNU Affero General Public License version 3 (see the file LICENSE).
*
* Sharing details widget
@@ -148,6 +148,58 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
].join(' ');
},
+ _snap_details_row_template: function() {
+ return [
+ '<tr id="shared-snap-{{id}}">',
+ ' <td>',
+ ' <span class="sortkey">{{id}}</span>',
+ ' <a class="sprite source-package-recipe" href="{{web_link}}">',
+ ' {{name}}',
+ ' </a>',
+ ' </td>',
+ ' <td class="action-icons nowrap">',
+ ' <span id="remove-snap-{{id}}">',
+ ' <a class="sprite remove action-icon" href="#"',
+ ' title="Unshare Snap {{name}} with {{displayname}}"',
+ ' data-self_link="{{self_link}}" data-name="{{name}}"',
+ ' data-type="snap">Remove</a>',
+ ' </span>',
+ ' </td>',
+ ' <td>',
+ ' <span class="information_type">',
+ ' {{information_type}}',
+ ' </span>',
+ ' </td>',
+ '</tr>'
+ ].join(' ');
+ },
+
+ _ocirecipe_details_row_template: function() {
+ return [
+ '<tr id="shared-ocirecipe-{{id}}">',
+ ' <td>',
+ ' <span class="sortkey">{{id}}</span>',
+ ' <a class="sprite source-package-recipe" href="{{web_link}}">',
+ ' {{name}}',
+ ' </a>',
+ ' </td>',
+ ' <td class="action-icons nowrap">',
+ ' <span id="remove-ocirecipe-{{id}}">',
+ ' <a class="sprite remove action-icon" href="#"',
+ ' title="Unshare OCI recipe {{name}} with {{displayname}}"',
+ ' data-self_link="{{self_link}}" data-name="{{name}}"',
+ ' data-type="ocirecipe">Remove</a>',
+ ' </span>',
+ ' </td>',
+ ' <td>',
+ ' <span class="information_type">',
+ ' {{information_type}}',
+ ' </span>',
+ ' </td>',
+ '</tr>'
+ ].join(' ');
+ },
+
_table_body_template: function() {
return [
'<tbody id="sharing-table-body">',
@@ -160,6 +212,12 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
'{{#gitrepositories}}',
'{{> gitrepository}}',
'{{/gitrepositories}}',
+ '{{#snaps}}',
+ '{{> snap}}',
+ '{{/snaps}}',
+ '{{#ocirecipes}}',
+ '{{> ocirecipe}}',
+ '{{/ocirecipes}}',
'{{#specifications}}',
'{{> spec}}',
'{{/specifications}}',
@@ -183,7 +241,7 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
// Delete the specified grantees from the table.
delete_artifacts: function(bugs, branches, gitrepositories, specifications,
- all_rows_deleted) {
+ snaps, ocirecipes, all_rows_deleted) {
var deleted_row_selectors = [];
var details_table_body = this.get('details_table_body');
Y.Array.each(bugs, function(bug) {
@@ -215,6 +273,20 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
deleted_row_selectors.push(selector);
}
});
+ Y.Array.each(snaps, function(snap) {
+ var selector = 'tr[id=shared-snap-' + snap.id + ']';
+ var table_row = details_table_body.one(selector);
+ if (Y.Lang.isValue(table_row)) {
+ deleted_row_selectors.push(selector);
+ }
+ });
+ Y.Array.each(ocirecipes, function(ocirecipe) {
+ var selector = 'tr[id=shared-ocirecipe-' + ocirecipe.id + ']';
+ var table_row = details_table_body.one(selector);
+ if (Y.Lang.isValue(table_row)) {
+ deleted_row_selectors.push(selector);
+ }
+ });
if (deleted_row_selectors.length === 0) {
return;
@@ -232,7 +304,7 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
.appendChild('<td colspan="3"></td>')
.setContent(
"There are no shared bugs, Bazaar branches, " +
- "Git repositories, or blueprints.");
+ "Git repositories, Snaps, OCI recipes or blueprints.");
}
};
var anim_duration = this.get('anim_duration');
@@ -275,6 +347,12 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
this.set(
'spec_details_row_template',
this._spec_details_row_template());
+ this.set(
+ 'snap_details_row_template',
+ this._snap_details_row_template());
+ this.set(
+ 'ocirecipe_details_row_template',
+ this._ocirecipe_details_row_template());
this.set(
'table_body_template',
@@ -288,16 +366,21 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
var gitrepositories = this.get('gitrepositories');
var bugs = this.get('bugs');
var specs = this.get('specifications');
+ var snaps = this.get('snaps');
+ var ocirecipes = this.get('ocirecipes');
if (bugs.length === 0 && branches.length === 0 &&
- gitrepositories.length === 0 && specs.length === 0 ) {
+ gitrepositories.length === 0 && specs.length === 0 &&
+ snaps.length === 0 && ocirecipes.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')
+ spec: this.get('spec_details_row_template'),
+ snap: this.get('snap_details_row_template'),
+ ocirecipe: this.get('ocirecipe_details_row_template')
};
var template = this.get('table_body_template');
var html = Y.lp.mustache.to_html(
@@ -307,6 +390,8 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
gitrepositories: gitrepositories,
bugs: bugs,
specifications: specs,
+ snaps: snaps,
+ ocirecipes: ocirecipes,
displayname: this.get('person_name')
},
partials);
@@ -344,14 +429,20 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
var existing_branches = this.get('branches');
var existing_gitrepositories = this.get('gitrepositories');
var existing_specifications = this.get('specifications');
+ var existing_snaps = this.get('snaps');
+ var existing_ocirecipes = this.get('ocirecipes');
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 model_snaps = LP.cache.snaps;
+ var model_ocirecipes = LP.cache.ocirecipes;
var deleted_bugs = [];
var deleted_branches = [];
var deleted_gitrepositories = [];
var deleted_specifications = [];
+ var deleted_snaps = [];
+ var deleted_ocirecipes = [];
var self = this;
Y.Array.each(existing_bugs, function(bug) {
@@ -388,12 +479,29 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
}
});
+ Y.Array.each(existing_snaps, function(snap) {
+ var model_snap = self._get_artifact_from_model(
+ snap.id, 'id', model_snaps);
+ if (!Y.Lang.isValue(model_snap)) {
+ deleted_snaps.push(snap);
+ }
+ });
+
+ Y.Array.each(existing_ocirecipes, function(ocirecipe) {
+ var model_ocirecipe = self._get_artifact_from_model(
+ ocirecipe.id, 'id', model_ocirecipes);
+ if (!Y.Lang.isValue(model_ocirecipe)) {
+ deleted_ocirecipes.push(ocirecipe);
+ }
+ });
+
if (deleted_bugs.length > 0 || deleted_branches.length > 0 ||
deleted_gitrepositories.length > 0 ||
- deleted_specifications.length > 0) {
+ deleted_specifications.length > 0 || deleted_snaps.length > 0 ||
+ deleted_ocirecipes.length > 0) {
this.delete_artifacts(
deleted_bugs, deleted_branches, deleted_gitrepositories,
- deleted_specifications,
+ deleted_specifications, deleted_snaps, deleted_ocirecipes,
model_bugs.length === 0 && model_branches.length === 0 &&
model_gitrepositories.length === 0 &&
deleted_specifications.length === 0);
@@ -403,6 +511,8 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
this.set('branches', model_branches);
this.set('gitrepositories', model_gitrepositories);
this.set('specifications', model_specifications);
+ this.set('snaps', model_snaps);
+ this.set('ocirecipes', model_ocirecipes);
Y.lp.app.sorttable.SortTable.registerSortKeyFunction(
'branchsortkey', this.branch_sort_key);
@@ -433,6 +543,14 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
value: null
},
+ snap_details_row_template: {
+ value: null
+ },
+
+ ocirecipe_details_row_template: {
+ value: null
+ },
+
branches: {
value: [],
// We clone the data passed in so external modifications do not
@@ -454,6 +572,20 @@ ns.SharingDetailsTable = Y.Base.create('sharingDetailsTable', Y.Widget, [], {
setter: clone_data
},
+ snaps: {
+ value: [],
+ // We clone the data passed in so external modifications do not
+ // interfere.
+ setter: clone_data
+ },
+
+ ocirecipes: {
+ value: [],
+ // We clone the data passed in so external modifications do not
+ // interfere.
+ setter: clone_data
+ },
+
// The node holding the details table.
details_table_body: {
getter: function() {
diff --git a/lib/lp/registry/javascript/sharing/sharingdetailsview.js b/lib/lp/registry/javascript/sharing/sharingdetailsview.js
index b41e9fe..d4197be 100644
--- a/lib/lp/registry/javascript/sharing/sharingdetailsview.js
+++ b/lib/lp/registry/javascript/sharing/sharingdetailsview.js
@@ -1,4 +1,4 @@
-/* Copyright 2012-2015 Canonical Ltd. This software is licensed under the
+/* Copyright 2012-2021 Canonical Ltd. This software is licensed under the
* GNU Affero General Public License version 3 (see the file LICENSE).
*
* Disclosure infrastructure.
@@ -32,6 +32,8 @@ Y.extend(SharingDetailsView, Y.Widget, {
bugs: LP.cache.bugs,
branches: LP.cache.branches,
gitrepositories: LP.cache.gitrepositories,
+ snaps: LP.cache.snaps,
+ ocirecipes: LP.cache.ocirecipes,
person_name: LP.cache.grantee.displayname,
specifications: LP.cache.specifications,
write_enabled: true
@@ -85,6 +87,22 @@ Y.extend(SharingDetailsView, Y.Widget, {
}
});
break;
+ case 'snap':
+ Y.Array.some(LP.cache.snaps, function(snap) {
+ if (snap.self_link === artifact_uri) {
+ artifact_id = snap.id;
+ return true;
+ }
+ });
+ break;
+ case 'ocirecipe':
+ Y.Array.some(LP.cache.ocirecipes, function(ocirecipe) {
+ if (ocirecipe.self_link === artifact_uri) {
+ artifact_id = ocirecipe.id;
+ return true;
+ }
+ });
+ break;
case 'spec':
Y.Array.some(LP.cache.specifications, function(spec) {
if (spec.self_link === artifact_uri) {
@@ -207,6 +225,22 @@ Y.extend(SharingDetailsView, Y.Widget, {
return true;
}
});
+ var snap_data = LP.cache.snaps;
+ Y.Array.some(snap_data, function(snap, index) {
+ if (snap.self_link === artifact_uri) {
+ snap_data.splice(index, 1);
+ self.syncUI();
+ return true;
+ }
+ });
+ var ocirecipe_data = LP.cache.ocirecipes;
+ Y.Array.some(ocirecipe_data, function(ocirecipe, index) {
+ if (ocirecipe.self_link === artifact_uri) {
+ ocirecipe_data.splice(index, 1);
+ self.syncUI();
+ return true;
+ }
+ });
},
/**
@@ -223,6 +257,8 @@ Y.extend(SharingDetailsView, Y.Widget, {
var branches = [];
var gitrepositories = [];
var specifications = [];
+ var snaps = [];
+ var ocirecipes = [];
switch (artifact_type) {
case 'bug':
bugs = [artifact_uri];
@@ -233,6 +269,12 @@ Y.extend(SharingDetailsView, Y.Widget, {
case 'gitrepository':
gitrepositories = [artifact_uri];
break;
+ case 'snap':
+ snaps = [artifact_uri];
+ break;
+ case 'ocirecipe':
+ ocirecipes = [artifact_uri];
+ break;
case 'spec':
specifications = [artifact_uri];
break;
@@ -256,6 +298,8 @@ Y.extend(SharingDetailsView, Y.Widget, {
bugs: bugs,
branches: branches,
gitrepositories: gitrepositories,
+ snaps: snaps,
+ ocirecipes: ocirecipes,
specifications: specifications
}
};
diff --git a/lib/lp/registry/javascript/sharing/tests/test_sharingdetails.js b/lib/lp/registry/javascript/sharing/tests/test_sharingdetails.js
index 7b7d306..30ee62a 100644
--- a/lib/lp/registry/javascript/sharing/tests/test_sharingdetails.js
+++ b/lib/lp/registry/javascript/sharing/tests/test_sharingdetails.js
@@ -1,4 +1,4 @@
-/* Copyright 2012-2015 Canonical Ltd. This software is licensed under the
+/* Copyright 2012-2021 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) {
@@ -44,6 +44,26 @@ YUI.add('lp.registry.sharing.sharingdetails.test', function(Y) {
repository_name: '~someone/+git/somerepo',
information_type: 'Private'
}
+ ],
+ snaps: [
+ {
+ self_link: 'api/devel/~someone/+snap/somesnap',
+ web_link: '/~someone/+snap/somesnap',
+ id: '2',
+ name: 'snap-name',
+ information_type: 'Private'
+ }
+ ],
+ ocirecipes: [
+ {
+ self_link: ('api/devel/~someone/proj/+oci/' +
+ 'ociproj/+recipe/recipe'),
+ web_link: ('~someone/proj/+oci/' +
+ 'ociproj/+recipe/recipe'),
+ name: 'ocirecipe-name',
+ id: '2',
+ information_type: 'Private'
+ }
]
}
};
@@ -71,6 +91,8 @@ YUI.add('lp.registry.sharing.sharingdetails.test', function(Y) {
bugs: window.LP.cache.bugs,
branches: window.LP.cache.branches,
gitrepositories: window.LP.cache.gitrepositories,
+ snaps: window.LP.cache.snaps,
+ ocirecipes: window.LP.cache.ocirecipes,
write_enabled: true
}, overrides);
window.LP.cache.grantee_data = config.grantees;
@@ -326,10 +348,13 @@ YUI.add('lp.registry.sharing.sharingdetails.test', function(Y) {
[window.LP.cache.bugs[0]],
[window.LP.cache.branches[0]],
[window.LP.cache.gitrepositories[0]],
+ [], // specs
+ [window.LP.cache.snaps[0]],
+ [window.LP.cache.ocirecipes[0]],
true);
Y.Assert.areEqual(
'There are no shared bugs, Bazaar branches, Git ' +
- 'repositories, or blueprints.',
+ 'repositories, Snaps, OCI recipes or blueprints.',
Y.one('#sharing-table-body tr').get('text'));
},
diff --git a/lib/lp/registry/javascript/sharing/tests/test_sharingdetailsview.js b/lib/lp/registry/javascript/sharing/tests/test_sharingdetailsview.js
index 84f7930..bdcdcfb 100644
--- a/lib/lp/registry/javascript/sharing/tests/test_sharingdetailsview.js
+++ b/lib/lp/registry/javascript/sharing/tests/test_sharingdetailsview.js
@@ -1,4 +1,4 @@
-/* Copyright 2012-2015 Canonical Ltd. This software is licensed under the
+/* Copyright 2012-2021 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) {
@@ -49,6 +49,26 @@ YUI.add('lp.registry.sharing.sharingdetailsview.test', function (Y) {
web_link: "/obsolete-junk/+spec/big-project"
}
],
+ snaps: [
+ {
+ self_link: 'api/devel/~someone/+snap/somesnap',
+ web_link: '/~someone/+snap/somesnap',
+ id: '2',
+ name: 'snap-name',
+ information_type: 'Private'
+ }
+ ],
+ ocirecipes: [
+ {
+ self_link: ('api/devel/~someone/proj/+oci/' +
+ 'ociproj/+recipe/recipe'),
+ web_link: ('~someone/proj/+oci/' +
+ 'ociproj/+recipe/recipe'),
+ name: 'ocirecipe-name',
+ id: '2',
+ information_type: 'Private'
+ }
+ ],
grantee: {
displayname: 'Fred Bloggs',
self_link: '~fred'
@@ -185,6 +205,51 @@ YUI.add('lp.registry.sharing.sharingdetailsview.test', function (Y) {
Y.Assert.isTrue(confirmRemove_called);
},
+ // Clicking a Snap remove link calls the
+ // confirm_grant_removal method with the correct parameters.
+ test_remove_snap_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/+snap/somesnap', artifact_uri);
+ Y.Assert.areEqual('snap-name', artifact_name);
+ Y.Assert.areEqual('snap', 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-snap-2] a');
+ delete_link_to_click.simulate('click');
+ Y.Assert.isTrue(confirmRemove_called);
+ },
+
+ // Clicking an OCI recipe remove link calls the
+ // confirm_grant_removal method with the correct parameters.
+ test_remove_ocirecipe_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/proj/+oci/ociproj/+recipe/recipe',
+ artifact_uri);
+ Y.Assert.areEqual('ocirecipe-name', artifact_name);
+ Y.Assert.areEqual('ocirecipe', 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-ocirecipe-2] a');
+ delete_link_to_click.simulate('click');
+ Y.Assert.isTrue(confirmRemove_called);
+ },
+
//Test the behaviour of the removal confirmation dialog.
_test_confirm_grant_removal: function(click_ok) {
this.view = this._create_Widget();
@@ -342,6 +407,88 @@ YUI.add('lp.registry.sharing.sharingdetailsview.test', function (Y) {
Y.Assert.isTrue(remove_grant_success_called);
},
+ // The perform_remove_grant method makes the expected XHR calls when a
+ // Snap grant remove link is clicked.
+ test_perform_remove_snap_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;
+ this.view.remove_grant_success = function(artifact_uri) {
+ Y.Assert.areEqual(
+ 'api/devel/~someone/+snap/somesnap', artifact_uri);
+ remove_grant_success_called = true;
+ };
+ var delete_link =
+ Y.one('#sharing-table-body span[id=remove-snap-2] a');
+ this.view.perform_remove_grant(
+ delete_link, 'api/devel/~someone/+snap/somesnap',
+ 'snap');
+ 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, 'snaps',
+ 'api/devel/~someone/+snap/somesnap');
+ Y.Assert.areEqual(expected_qs, mockio.last_request.config.data);
+ mockio.last_request.successJSON({});
+ Y.Assert.isTrue(remove_grant_success_called);
+ },
+
+ // The perform_remove_grant method makes the expected XHR calls when an
+ // OCI recipe grant remove link is clicked.
+ test_perform_remove_ocirecipe_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;
+ this.view.remove_grant_success = function(artifact_uri) {
+ Y.Assert.areEqual(
+ 'api/devel/~someone/proj/+oci/ociproj/+recipe/recipe',
+ artifact_uri);
+ remove_grant_success_called = true;
+ };
+ var delete_link =
+ Y.one('#sharing-table-body span[id=remove-ocirecipe-2] a');
+ this.view.perform_remove_grant(
+ delete_link,
+ 'api/devel/~someone/proj/+oci/ociproj/+recipe/recipe',
+ 'ocirecipe');
+ 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, 'ocirecipes',
+ 'api/devel/~someone/proj/+oci/ociproj/+recipe/recipe');
+ 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});
@@ -410,6 +557,40 @@ YUI.add('lp.registry.sharing.sharingdetailsview.test', function (Y) {
'All specs are removed from the cache.');
},
+ // The remove Snap grant callback updates the model and
+ // syncs the UI.
+ test_remove_snap_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/+snap/somesnap');
+ Y.Assert.isTrue(syncUI_called);
+ Y.Array.each(window.LP.cache.snaps, function(snap) {
+ Y.Assert.areNotEqual(2, snap.id);
+ });
+ },
+
+ // The remove OCI recipe grant callback updates the model and
+ // syncs the UI.
+ test_remove_ocirecipe_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/proj/+oci/ociproj/+recipe/recipe');
+ Y.Assert.isTrue(syncUI_called);
+ Y.Array.each(window.LP.cache.ocirecipes, function(recipe) {
+ Y.Assert.areNotEqual(2, recipe.id);
+ });
+ },
+
// XHR calls display errors correctly.
_assert_error_displayed_on_failure: function(
artifact, invoke_operation) {
@@ -491,6 +672,34 @@ YUI.add('lp.registry.sharing.sharingdetailsview.test', function (Y) {
};
this._assert_error_displayed_on_failure('spec', invoke_remove);
},
+
+ // The perform_remove_grant method handles errors correctly with Snaps.
+ test_perform_remove_snap_error: function() {
+ var invoke_remove = function(view) {
+ var delete_link =
+ Y.one('#sharing-table-body span[id=remove-snap-2] a');
+ view.perform_remove_grant(
+ delete_link, 'api/devel/~someone/+snap/somesnap',
+ 'snap');
+ };
+ this._assert_error_displayed_on_failure('snap', invoke_remove);
+ },
+
+ // The perform_remove_grant method handles errors correctly with OCI
+ // recipes.
+ test_perform_remove_ocirecipe_error: function() {
+ var invoke_remove = function(view) {
+ var delete_link =
+ Y.one('#sharing-table-body span[id=remove-ocirecipe-2] a');
+ view.perform_remove_grant(
+ delete_link,
+ 'api/devel/~someone/proj/+oci/ociproj/+recipe/recipe',
+ 'ocirecipe');
+ };
+ this._assert_error_displayed_on_failure(
+ 'ocirecipe', invoke_remove);
+ },
+
// Test that syncUI works as expected.
test_syncUI: function() {
this.view = this._create_Widget();
diff --git a/lib/lp/registry/services/sharingservice.py b/lib/lp/registry/services/sharingservice.py
index 4927dc4..f52575c 100644
--- a/lib/lp/registry/services/sharingservice.py
+++ b/lib/lp/registry/services/sharingservice.py
@@ -865,7 +865,7 @@ class SharingService:
# Create a job to remove subscriptions for artifacts the grantee can no
# longer see.
- return getUtility(IRemoveArtifactSubscriptionsJobSource).create(
+ getUtility(IRemoveArtifactSubscriptionsJobSource).create(
user, artifacts, grantee=grantee, pillar=pillar)
def ensureAccessGrants(self, grantees, user, bugs=None, branches=None,
diff --git a/lib/lp/registry/templates/pillar-sharing-details.pt b/lib/lp/registry/templates/pillar-sharing-details.pt
index a6f2710..0b89496 100644
--- a/lib/lp/registry/templates/pillar-sharing-details.pt
+++ b/lib/lp/registry/templates/pillar-sharing-details.pt
@@ -26,20 +26,31 @@
<div metal:fill-slot="main">
<div id="observer-summary">
- <p>
- <tal:bugs replace="view/shared_bugs_count">0</tal:bugs> bugs,
- <tal:branches replace="view/shared_branches_count">0</tal:branches> Bazaar 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,
- Bazaar branches, Git repositories, and blueprints.
+ Bazaar branches, Git repositories, Snaps, OCI recipes and blueprints.
</tal:is-team>
- </p>
+ Shared with <tal:name replace="view/person/displayname">grantee</tal:name>
+ <ul class="listing">
+ <li tal:condition="view/shared_bugs_count">
+ <span tal:replace="view/shared_bugs_count" /> Bugs
+ </li>
+ <li tal:condition="view/shared_branches_count">
+ <span tal:replace="view/shared_branches_count" /> Bazaar branches
+ </li>
+ <li tal:condition="view/shared_gitrepositories_count">
+ <span tal:replace="view/shared_gitrepositories_count" /> Git repositories
+ </li>
+ <li tal:condition="view/shared_ocirecipe_count">
+ <span tal:replace="view/shared_ocirecipe_count" /> OCI recipes
+ </li>
+ <li tal:condition="view/shared_snaps_count">
+ <span tal:replace="view/shared_snaps_count" /> Snap recipes
+ </li>
+ <li tal:condition="view/shared_specifications_count">
+ <span tal:replace="view/shared_specifications_count" /> Blueprints
+ </li>
+ </ul>
</div>
<table id="shared-table" class="listing sortable">
@@ -49,7 +60,8 @@
<thead>
<tr>
<th colspan="2" width="">
- Subscribed Bug Report, Bazaar Branch, Git Repository, or Blueprint
+ Subscribed Bug Report, Bazaar Branch, Git Repository, Snaps, OCI
+ recipes or Blueprint
</th>
<th>
Information Type
@@ -60,7 +72,7 @@
<tr>
<td colspan="3">
There are no shared bugs, Bazaar branches, Git repositories,
- or blueprints.
+ Snaps, OCI recipes or blueprints.
</td>
</tr>
</tbody>
Follow ups