Merge lp:~cjwatson/launchpad/snap-find-by-store-name into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 19030
Proposed branch: lp:~cjwatson/launchpad/snap-find-by-store-name
Merge into: lp:launchpad
Diff against target: 297 lines (+152/-3)
4 files modified
database/schema/patch-2210-05-0.sql (+9/-0)
lib/lp/snappy/interfaces/snap.py (+17/-0)
lib/lp/snappy/model/snap.py (+11/-3)
lib/lp/snappy/tests/test_snap.py (+115/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-find-by-store-name
Reviewer Review Type Date Requested Status
William Grant code db Approve
Review via email: mp+370562@code.launchpad.net

Commit message

Add SnapSet.findByStoreName.

Description of the change

This will be useful for the next iteration of build.snapcraft.io, and is handy for ad-hoc operational queries as well.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code db)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'database/schema/patch-2210-05-0.sql'
--- database/schema/patch-2210-05-0.sql 1970-01-01 00:00:00 +0000
+++ database/schema/patch-2210-05-0.sql 2019-08-22 10:39:42 +0000
@@ -0,0 +1,9 @@
1-- Copyright 2019 Canonical Ltd. This software is licensed under the
2-- GNU Affero General Public License version 3 (see the file LICENSE).
3
4SET client_min_messages=ERROR;
5
6CREATE INDEX snap__git_repository_url__idx ON Snap (git_repository_url);
7CREATE INDEX snap__store_name__idx ON Snap (store_name);
8
9INSERT INTO LaunchpadDatabaseRevision VALUES (2210, 05, 0);
010
=== modified file 'lib/lp/snappy/interfaces/snap.py'
--- lib/lp/snappy/interfaces/snap.py 2019-06-21 11:30:26 +0000
+++ lib/lp/snappy/interfaces/snap.py 2019-08-22 10:39:42 +0000
@@ -1005,6 +1005,23 @@
1005 this user; otherwise, only return publicly-visible packages.1005 this user; otherwise, only return publicly-visible packages.
1006 """1006 """
10071007
1008 @operation_parameters(
1009 store_name=TextLine(
1010 title=_("The registered store package name to search for.")),
1011 owner=Reference(IPerson, title=_("Owner"), required=False))
1012 @call_with(visible_by_user=REQUEST_USER)
1013 @operation_returns_collection_of(ISnap)
1014 @export_read_operation()
1015 @operation_for_version("devel")
1016 def findByStoreName(store_name, owner=None, visible_by_user=None):
1017 """Return all snap packages with the given store package name.
1018
1019 :param store_name: A registered store package name.
1020 :param owner: Only return packages owned by this user.
1021 :param visible_by_user: If not None, only return packages visible by
1022 this user; otherwise, only return publicly-visible packages.
1023 """
1024
1008 def preloadDataForSnaps(snaps, user):1025 def preloadDataForSnaps(snaps, user):
1009 """Load the data related to a list of snap packages."""1026 """Load the data related to a list of snap packages."""
10101027
10111028
=== modified file 'lib/lp/snappy/model/snap.py'
--- lib/lp/snappy/model/snap.py 2019-08-21 10:36:27 +0000
+++ lib/lp/snappy/model/snap.py 2019-08-22 10:39:42 +0000
@@ -1244,7 +1244,7 @@
1244 snaps.order_by(Desc(Snap.date_last_modified))1244 snaps.order_by(Desc(Snap.date_last_modified))
1245 return snaps1245 return snaps
12461246
1247 def _findByURLVisibilityClause(self, visible_by_user):1247 def _findSnapVisibilityClause(self, visible_by_user):
1248 # XXX cjwatson 2016-11-25: This is in principle a poor query, but we1248 # XXX cjwatson 2016-11-25: This is in principle a poor query, but we
1249 # don't yet have the access grant infrastructure to do better, and1249 # don't yet have the access grant infrastructure to do better, and
1250 # in any case the numbers involved should be very small.1250 # in any case the numbers involved should be very small.
@@ -1266,7 +1266,7 @@
1266 clauses = [Snap.git_repository_url == url]1266 clauses = [Snap.git_repository_url == url]
1267 if owner is not None:1267 if owner is not None:
1268 clauses.append(Snap.owner == owner)1268 clauses.append(Snap.owner == owner)
1269 clauses.append(self._findByURLVisibilityClause(visible_by_user))1269 clauses.append(self._findSnapVisibilityClause(visible_by_user))
1270 return IStore(Snap).find(Snap, *clauses)1270 return IStore(Snap).find(Snap, *clauses)
12711271
1272 def findByURLPrefix(self, url_prefix, owner=None, visible_by_user=None):1272 def findByURLPrefix(self, url_prefix, owner=None, visible_by_user=None):
@@ -1283,7 +1283,15 @@
1283 clauses = [Or(*prefix_clauses)]1283 clauses = [Or(*prefix_clauses)]
1284 if owner is not None:1284 if owner is not None:
1285 clauses.append(Snap.owner == owner)1285 clauses.append(Snap.owner == owner)
1286 clauses.append(self._findByURLVisibilityClause(visible_by_user))1286 clauses.append(self._findSnapVisibilityClause(visible_by_user))
1287 return IStore(Snap).find(Snap, *clauses)
1288
1289 def findByStoreName(self, store_name, owner=None, visible_by_user=None):
1290 """See `ISnapSet`."""
1291 clauses = [Snap.store_name == store_name]
1292 if owner is not None:
1293 clauses.append(Snap.owner == owner)
1294 clauses.append(self._findSnapVisibilityClause(visible_by_user))
1287 return IStore(Snap).find(Snap, *clauses)1295 return IStore(Snap).find(Snap, *clauses)
12881296
1289 def preloadDataForSnaps(self, snaps, user=None):1297 def preloadDataForSnaps(self, snaps, user=None):
12901298
=== modified file 'lib/lp/snappy/tests/test_snap.py'
--- lib/lp/snappy/tests/test_snap.py 2019-08-21 10:36:27 +0000
+++ lib/lp/snappy/tests/test_snap.py 2019-08-22 10:39:42 +0000
@@ -1677,6 +1677,34 @@
1677 [snaps[0], snaps[2], snaps[4], snaps[6]],1677 [snaps[0], snaps[2], snaps[4], snaps[6]],
1678 getUtility(ISnapSet).findByURLPrefixes(prefixes, owner=owners[0]))1678 getUtility(ISnapSet).findByURLPrefixes(prefixes, owner=owners[0]))
16791679
1680 def test_findByStoreName(self):
1681 # ISnapSet.findByStoreName returns visible Snaps with the given
1682 # store name.
1683 store_names = ["foo", "bar"]
1684 owners = [self.factory.makePerson() for i in range(2)]
1685 snaps = []
1686 for store_name in store_names:
1687 for owner in owners:
1688 for private in (False, True):
1689 snaps.append(self.factory.makeSnap(
1690 registrant=owner, owner=owner, private=private,
1691 store_name=store_name))
1692 snaps.append(self.factory.makeSnap())
1693 self.assertContentEqual(
1694 [snaps[0], snaps[2]],
1695 getUtility(ISnapSet).findByStoreName(store_names[0]))
1696 with person_logged_in(owners[0]):
1697 self.assertContentEqual(
1698 snaps[:2],
1699 getUtility(ISnapSet).findByStoreName(
1700 store_names[0], owner=owners[0],
1701 visible_by_user=owners[0]))
1702 self.assertContentEqual(
1703 [snaps[2]],
1704 getUtility(ISnapSet).findByStoreName(
1705 store_names[0], owner=owners[1],
1706 visible_by_user=owners[0]))
1707
1680 def test_getSnapcraftYaml_bzr_snap_snapcraft_yaml(self):1708 def test_getSnapcraftYaml_bzr_snap_snapcraft_yaml(self):
1681 def getInventory(unique_name, dirname, *args, **kwargs):1709 def getInventory(unique_name, dirname, *args, **kwargs):
1682 if dirname == "snap":1710 if dirname == "snap":
@@ -2732,6 +2760,7 @@
2732 commercial_admin = (2760 commercial_admin = (
2733 getUtility(ILaunchpadCelebrities).commercial_admin.teamowner)2761 getUtility(ILaunchpadCelebrities).commercial_admin.teamowner)
2734 logout()2762 logout()
2763
2735 # Anonymous requests can only see public snaps.2764 # Anonymous requests can only see public snaps.
2736 anon_webservice = LaunchpadWebServiceCaller("test", "")2765 anon_webservice = LaunchpadWebServiceCaller("test", "")
2737 response = anon_webservice.named_get(2766 response = anon_webservice.named_get(
@@ -2741,6 +2770,7 @@
2741 self.assertContentEqual(2770 self.assertContentEqual(
2742 [ws_snaps[0]],2771 [ws_snaps[0]],
2743 [entry["self_link"] for entry in response.jsonBody()["entries"]])2772 [entry["self_link"] for entry in response.jsonBody()["entries"]])
2773
2744 # persons[0] can see their own private snap as well, but not those2774 # persons[0] can see their own private snap as well, but not those
2745 # for other people.2775 # for other people.
2746 webservice = webservice_for_person(2776 webservice = webservice_for_person(
@@ -2759,6 +2789,7 @@
2759 self.assertContentEqual(2789 self.assertContentEqual(
2760 [ws_snaps[2]],2790 [ws_snaps[2]],
2761 [entry["self_link"] for entry in response.jsonBody()["entries"]])2791 [entry["self_link"] for entry in response.jsonBody()["entries"]])
2792
2762 # Admins can see all snaps.2793 # Admins can see all snaps.
2763 commercial_admin_webservice = webservice_for_person(2794 commercial_admin_webservice = webservice_for_person(
2764 commercial_admin, permission=OAuthPermission.READ_PRIVATE)2795 commercial_admin, permission=OAuthPermission.READ_PRIVATE)
@@ -2797,6 +2828,7 @@
2797 commercial_admin = (2828 commercial_admin = (
2798 getUtility(ILaunchpadCelebrities).commercial_admin.teamowner)2829 getUtility(ILaunchpadCelebrities).commercial_admin.teamowner)
2799 logout()2830 logout()
2831
2800 # Anonymous requests can only see public snaps.2832 # Anonymous requests can only see public snaps.
2801 anon_webservice = LaunchpadWebServiceCaller("test", "")2833 anon_webservice = LaunchpadWebServiceCaller("test", "")
2802 response = anon_webservice.named_get(2834 response = anon_webservice.named_get(
@@ -2812,6 +2844,7 @@
2812 self.assertContentEqual(2844 self.assertContentEqual(
2813 [ws_snaps[0]],2845 [ws_snaps[0]],
2814 [entry["self_link"] for entry in response.jsonBody()["entries"]])2846 [entry["self_link"] for entry in response.jsonBody()["entries"]])
2847
2815 # persons[0] can see both public snaps with this URL, as well as2848 # persons[0] can see both public snaps with this URL, as well as
2816 # their own private snap.2849 # their own private snap.
2817 webservice = webservice_for_person(2850 webservice = webservice_for_person(
@@ -2829,6 +2862,7 @@
2829 self.assertContentEqual(2862 self.assertContentEqual(
2830 ws_snaps[:2],2863 ws_snaps[:2],
2831 [entry["self_link"] for entry in response.jsonBody()["entries"]])2864 [entry["self_link"] for entry in response.jsonBody()["entries"]])
2865
2832 # Admins can see all snaps with this URL.2866 # Admins can see all snaps with this URL.
2833 commercial_admin_webservice = webservice_for_person(2867 commercial_admin_webservice = webservice_for_person(
2834 commercial_admin, permission=OAuthPermission.READ_PRIVATE)2868 commercial_admin, permission=OAuthPermission.READ_PRIVATE)
@@ -2873,6 +2907,7 @@
2873 getUtility(ILaunchpadCelebrities).commercial_admin.teamowner)2907 getUtility(ILaunchpadCelebrities).commercial_admin.teamowner)
2874 logout()2908 logout()
2875 prefix = "https://git.example.org/foo/"2909 prefix = "https://git.example.org/foo/"
2910
2876 # Anonymous requests can only see public snaps.2911 # Anonymous requests can only see public snaps.
2877 anon_webservice = LaunchpadWebServiceCaller("test", "")2912 anon_webservice = LaunchpadWebServiceCaller("test", "")
2878 response = anon_webservice.named_get(2913 response = anon_webservice.named_get(
@@ -2889,6 +2924,7 @@
2889 self.assertContentEqual(2924 self.assertContentEqual(
2890 [ws_snaps[i] for i in (0, 4)],2925 [ws_snaps[i] for i in (0, 4)],
2891 [entry["self_link"] for entry in response.jsonBody()["entries"]])2926 [entry["self_link"] for entry in response.jsonBody()["entries"]])
2927
2892 # persons[0] can see all public snaps with this URL prefix, as well2928 # persons[0] can see all public snaps with this URL prefix, as well
2893 # as their own matching private snaps.2929 # as their own matching private snaps.
2894 webservice = webservice_for_person(2930 webservice = webservice_for_person(
@@ -2907,6 +2943,7 @@
2907 self.assertContentEqual(2943 self.assertContentEqual(
2908 [ws_snaps[i] for i in (0, 1, 4, 5)],2944 [ws_snaps[i] for i in (0, 1, 4, 5)],
2909 [entry["self_link"] for entry in response.jsonBody()["entries"]])2945 [entry["self_link"] for entry in response.jsonBody()["entries"]])
2946
2910 # Admins can see all snaps with this URL prefix.2947 # Admins can see all snaps with this URL prefix.
2911 commercial_admin_webservice = webservice_for_person(2948 commercial_admin_webservice = webservice_for_person(
2912 commercial_admin, permission=OAuthPermission.READ_PRIVATE)2949 commercial_admin, permission=OAuthPermission.READ_PRIVATE)
@@ -2955,6 +2992,7 @@
2955 logout()2992 logout()
2956 prefixes = [2993 prefixes = [
2957 "https://git.example.org/foo/", "https://git.example.org/bar/"]2994 "https://git.example.org/foo/", "https://git.example.org/bar/"]
2995
2958 # Anonymous requests can only see public snaps.2996 # Anonymous requests can only see public snaps.
2959 anon_webservice = LaunchpadWebServiceCaller("test", "")2997 anon_webservice = LaunchpadWebServiceCaller("test", "")
2960 response = anon_webservice.named_get(2998 response = anon_webservice.named_get(
@@ -2971,6 +3009,7 @@
2971 self.assertContentEqual(3009 self.assertContentEqual(
2972 [ws_snaps[i] for i in (0, 4, 8, 12)],3010 [ws_snaps[i] for i in (0, 4, 8, 12)],
2973 [entry["self_link"] for entry in response.jsonBody()["entries"]])3011 [entry["self_link"] for entry in response.jsonBody()["entries"]])
3012
2974 # persons[0] can see all public snaps with any of these URL3013 # persons[0] can see all public snaps with any of these URL
2975 # prefixes, as well as their own matching private snaps.3014 # prefixes, as well as their own matching private snaps.
2976 webservice = webservice_for_person(3015 webservice = webservice_for_person(
@@ -2989,6 +3028,7 @@
2989 self.assertContentEqual(3028 self.assertContentEqual(
2990 [ws_snaps[i] for i in (0, 1, 4, 5, 8, 9, 12, 13)],3029 [ws_snaps[i] for i in (0, 1, 4, 5, 8, 9, 12, 13)],
2991 [entry["self_link"] for entry in response.jsonBody()["entries"]])3030 [entry["self_link"] for entry in response.jsonBody()["entries"]])
3031
2992 # Admins can see all snaps with any of these URL prefixes.3032 # Admins can see all snaps with any of these URL prefixes.
2993 commercial_admin_webservice = webservice_for_person(3033 commercial_admin_webservice = webservice_for_person(
2994 commercial_admin, permission=OAuthPermission.READ_PRIVATE)3034 commercial_admin, permission=OAuthPermission.READ_PRIVATE)
@@ -3007,6 +3047,81 @@
3007 [ws_snaps[i] for i in (0, 1, 4, 5, 8, 9, 12, 13)],3047 [ws_snaps[i] for i in (0, 1, 4, 5, 8, 9, 12, 13)],
3008 [entry["self_link"] for entry in response.jsonBody()["entries"]])3048 [entry["self_link"] for entry in response.jsonBody()["entries"]])
30093049
3050 def test_findByStoreName(self):
3051 # lp.snaps.findByStoreName returns visible Snaps with the given
3052 # store name.
3053 persons = [self.factory.makePerson(), self.factory.makePerson()]
3054 store_names = ["foo", "bar"]
3055 snaps = []
3056 for store_name in store_names:
3057 for person in persons:
3058 for private in (False, True):
3059 snaps.append(self.factory.makeSnap(
3060 registrant=person, owner=person, private=private,
3061 store_name=store_name))
3062 with admin_logged_in():
3063 person_urls = [api_url(person) for person in persons]
3064 ws_snaps = [
3065 self.webservice.getAbsoluteUrl(api_url(snap))
3066 for snap in snaps]
3067 commercial_admin = (
3068 getUtility(ILaunchpadCelebrities).commercial_admin.teamowner)
3069 logout()
3070
3071 # Anonymous requests can only see public snaps.
3072 anon_webservice = LaunchpadWebServiceCaller("test", "")
3073 response = anon_webservice.named_get(
3074 "/+snaps", "findByStoreName", store_name=store_names[0],
3075 api_version="devel")
3076 self.assertEqual(200, response.status)
3077 self.assertContentEqual(
3078 [ws_snaps[0], ws_snaps[2]],
3079 [entry["self_link"] for entry in response.jsonBody()["entries"]])
3080 response = anon_webservice.named_get(
3081 "/+snaps", "findByStoreName", store_name=store_names[0],
3082 owner=person_urls[0], api_version="devel")
3083 self.assertEqual(200, response.status)
3084 self.assertContentEqual(
3085 [ws_snaps[0]],
3086 [entry["self_link"] for entry in response.jsonBody()["entries"]])
3087
3088 # persons[0] can see both public snaps with this store name, as well
3089 # as their own private snap.
3090 webservice = webservice_for_person(
3091 persons[0], permission=OAuthPermission.READ_PRIVATE)
3092 response = webservice.named_get(
3093 "/+snaps", "findByStoreName", store_name=store_names[0],
3094 api_version="devel")
3095 self.assertEqual(200, response.status)
3096 self.assertContentEqual(
3097 ws_snaps[:3],
3098 [entry["self_link"] for entry in response.jsonBody()["entries"]])
3099 response = webservice.named_get(
3100 "/+snaps", "findByStoreName", store_name=store_names[0],
3101 owner=person_urls[0], api_version="devel")
3102 self.assertEqual(200, response.status)
3103 self.assertContentEqual(
3104 ws_snaps[:2],
3105 [entry["self_link"] for entry in response.jsonBody()["entries"]])
3106
3107 # Admins can see all snaps with this store name.
3108 commercial_admin_webservice = webservice_for_person(
3109 commercial_admin, permission=OAuthPermission.READ_PRIVATE)
3110 response = commercial_admin_webservice.named_get(
3111 "/+snaps", "findByStoreName", store_name=store_names[0],
3112 api_version="devel")
3113 self.assertEqual(200, response.status)
3114 self.assertContentEqual(
3115 ws_snaps[:4],
3116 [entry["self_link"] for entry in response.jsonBody()["entries"]])
3117 response = commercial_admin_webservice.named_get(
3118 "/+snaps", "findByStoreName", store_name=store_names[0],
3119 owner=person_urls[0], api_version="devel")
3120 self.assertEqual(200, response.status)
3121 self.assertContentEqual(
3122 ws_snaps[:2],
3123 [entry["self_link"] for entry in response.jsonBody()["entries"]])
3124
3010 def setProcessors(self, user, snap, names):3125 def setProcessors(self, user, snap, names):
3011 ws = webservice_for_person(3126 ws = webservice_for_person(
3012 user, permission=OAuthPermission.WRITE_PUBLIC)3127 user, permission=OAuthPermission.WRITE_PUBLIC)