Skip to content

Commit 51410fe

Browse files
berinhardewdurbin
andauthored
Move sponsors to DB (#9512)
* Create Sponsor model * Service column can be empty * Command to load sponsors to database * initdb should load sponsors * Add sqlalchemy-utils as a dependency * Use URL type as fields and refactor to have a single migration * Populate images URL using current pypi static as the URL prefix * Fix code lint warnings I added #noqa for a few of the activity strings from the sponsors. * Add direnv's .envrc to gitignore I'm used to use direnv to configure env variables. I had to create it to always have WAREHOUSE_IPYTHON_SHELL enabled. * Change sponsors page to read data from db instead of HTML include * Fix routes test * Unit test sponsors page * Better name for url field * Replace pickle by pg's array type * Update migration file * Revert "Unit test sponsors page" This reverts commit 8d5b1cc. * Revert "Fix routes test" This reverts commit 582581f. * Revert "Change sponsors page to read data from db instead of HTML include" This reverts commit c5f0d7a. * Test utils for sponsors app * Add sponsors list to all requests (except for static files) * Update templates to read sponsors from request variable * Reformat * Camoify logos * Add minimal sponsor list to admin * Add link to sponsors list * Add minimal edit page * Reformat * Simple admin view to create sponsors * Unit test sponsors admin * Update migration to define activity as a markdown field * Update code do handle activity as markdown * Add missing pretend calls to admin routes test * Fix flake8 lint warnings * Add new is_active flag to control sponsors display * Only display active sponsors * Add is_active boolean to sponsors admin * Add feedback message after sponsor update * Color logo is required by the admin * Do not validate footer sponsor without white logo * Implement new view to delete a sponsor * Add modal to confirm sponsor before deletion * Run make translations to fix CI * Footer sponsors can be only infra sponsors as well * Increase test coverage for sponsors/models.py * 100% test coverage at cli/sponsors.py * Add simple unit test to make explicit validation done by readme lib * Add sponsors app to the docs * Add new boolean flag is_psf_staff to user model * Introduce PSF Staff group and permission * Configure permissions considering PSF staff members * Use SQL false function instead * Refactor permissions introducing a base one to guarantee admin access * Rename admin dashboard access permission Co-authored-by: Ee W. Durbin III <[email protected]>
1 parent cc04f84 commit 51410fe

39 files changed

+1856
-618
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
.coverage
99
.state
1010
.idea
11+
.envrc
1112

1213
docs/_build/
1314
build/

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ initdb:
147147
xz -d -f -k dev/$(DB).sql.xz --stdout | docker-compose run --rm web psql -h db -d warehouse -U postgres -v ON_ERROR_STOP=1 -1 -f -
148148
docker-compose run --rm web python -m warehouse db upgrade head
149149
$(MAKE) reindex
150+
docker-compose run web python -m warehouse sponsors populate-db
150151

151152
reindex:
152153
docker-compose run --rm web python -m warehouse search reindex

docs/application.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ Directories within the repository:
110110
- `rss/ <https://github.com/pypa/warehouse/tree/master/warehouse/rss>`_ - RSS feeds: :doc:`api-reference/feeds`
111111
- `search/ <https://github.com/pypa/warehouse/tree/master/warehouse/search>`_ - utilities for building and querying the search index
112112
- `sitemap/ <https://github.com/pypa/warehouse/tree/master/warehouse/sitemap>`_ - site maps
113+
- `sponsors/ <https://github.com/pypa/warehouse/tree/master/warehouse/sponsors>`_ - sponsors management
113114
- `templates/ <https://github.com/pypa/warehouse/tree/master/warehouse/templates>`_ - Jinja templates for web pages, emails, etc.
114115
- `utils/ <https://github.com/pypa/warehouse/tree/master/warehouse/utils>`_ - various utilities Warehouse uses
115116

requirements/main.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ sentry-sdk
5050
setuptools
5151
sqlalchemy>=0.9,<1.4.0 # https://github.com/pypa/warehouse/pull/9228
5252
sqlalchemy-citext
53+
sqlalchemy-utils
5354
stdlib-list
5455
structlog
5556
transaction

requirements/main.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -875,11 +875,16 @@ six==1.15.0 \
875875
# pyopenssl
876876
# python-dateutil
877877
# readme-renderer
878+
# sqlalchemy-utils
878879
# tenacity
879880
# webauthn
880881
sqlalchemy-citext==1.8.0 \
881882
--hash=sha256:a1740e693a9a334e7c8f60ae731083fe75ce6c1605bb9ca6644a6f1f63b15b77
882-
# via -r requirements/main.in
883+
# via -r main.in
884+
sqlalchemy-utils==0.37.2 \
885+
--hash=sha256:042e08454ee7b822b1e2f2b7c20f76fe7b8255de10354718a11e68ced1a64643 \
886+
--hash=sha256:c60b8b43c9ef809d147b0bc571cfbfe0992f854ec242bc01ab7a562f76113743
887+
# via -r main.in
883888
sqlalchemy==1.3.23 \
884889
--hash=sha256:040bdfc1d76a9074717a3f43455685f781c581f94472b010cd6c4754754e1862 \
885890
--hash=sha256:1fe5d8d39118c2b018c215c37b73fd6893c3e1d4895be745ca8ff6eb83333ed3 \
@@ -924,6 +929,7 @@ sqlalchemy==1.3.23 \
924929
# alembic
925930
# paginate-sqlalchemy
926931
# sqlalchemy-citext
932+
# sqlalchemy-utils
927933
# zope.sqlalchemy
928934
stdlib-list==0.8.0 \
929935
--hash=sha256:2ae0712a55b68f3fbbc9e58d6fa1b646a062188f49745b495f94d3310a9fdd3e \

tests/common/db/accounts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class Meta:
3030
is_active = True
3131
is_superuser = False
3232
is_moderator = False
33+
is_psf_staff = False
3334
date_joined = factory.fuzzy.FuzzyNaiveDateTime(
3435
datetime.datetime(2005, 1, 1), datetime.datetime(2010, 1, 1)
3536
)

tests/common/db/base.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,26 @@ def fuzz(self):
4646
chars = string.ascii_letters + string.digits
4747
username = "".join(random.choice(chars) for i in range(12))
4848
return "@".join([username, self.domain])
49+
50+
51+
class FuzzyList(fuzzy.BaseFuzzyAttribute):
52+
def __init__(self, item_factory, item_kwargs=None, size=1, *args, **kwargs):
53+
super().__init__(*args, **kwargs)
54+
self.item_factory = item_factory
55+
self.item_kwargs = item_kwargs or {}
56+
self.size = size
57+
58+
def fuzz(self):
59+
return [self.item_factory(**self.item_kwargs).fuzz() for i in range(self.size)]
60+
61+
62+
class FuzzyUrl(fuzzy.BaseFuzzyAttribute):
63+
def __init__(self, domain="example.com", is_secure=False, *args, **kwargs):
64+
super().__init__(*args, **kwargs)
65+
self.domain = domain
66+
self.protocol = "https" if is_secure else "http"
67+
68+
def fuzz(self):
69+
chars = string.ascii_letters
70+
path = "".join(random.choice(chars) for i in range(12))
71+
return f"{self.protocol}://{self.domain}/{path}"

tests/common/db/sponsors.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
13+
from factory import fuzzy
14+
15+
from warehouse.sponsors.models import Sponsor
16+
17+
from .base import FuzzyUrl, WarehouseFactory
18+
19+
20+
class SponsorFactory(WarehouseFactory):
21+
class Meta:
22+
model = Sponsor
23+
24+
name = fuzzy.FuzzyText(length=12)
25+
service = fuzzy.FuzzyText(length=12)
26+
activity_markdown = fuzzy.FuzzyText(length=12)
27+
28+
link_url = FuzzyUrl()
29+
color_logo_url = FuzzyUrl()
30+
white_logo_url = FuzzyUrl()
31+
32+
is_active = True
33+
footer = True
34+
psf_sponsor = True
35+
infra_sponsor = False
36+
one_time = False
37+
sidebar = True

tests/unit/accounts/test_core.py

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -227,16 +227,61 @@ def test_via_basic_auth_compromised(
227227

228228
class TestAuthenticate:
229229
@pytest.mark.parametrize(
230-
("is_superuser", "is_moderator", "expected"),
230+
("is_superuser", "is_moderator", "is_psf_staff", "expected"),
231231
[
232-
(False, False, []),
233-
(True, False, ["group:admins", "group:moderators"]),
234-
(False, True, ["group:moderators"]),
235-
(True, True, ["group:admins", "group:moderators"]),
232+
(False, False, False, []),
233+
(
234+
True,
235+
False,
236+
False,
237+
[
238+
"group:admins",
239+
"group:moderators",
240+
"group:psf_staff",
241+
"group:with_admin_dashboard_access",
242+
],
243+
),
244+
(
245+
False,
246+
True,
247+
False,
248+
["group:moderators", "group:with_admin_dashboard_access"],
249+
),
250+
(
251+
True,
252+
True,
253+
False,
254+
[
255+
"group:admins",
256+
"group:moderators",
257+
"group:psf_staff",
258+
"group:with_admin_dashboard_access",
259+
],
260+
),
261+
(
262+
False,
263+
False,
264+
True,
265+
["group:psf_staff", "group:with_admin_dashboard_access"],
266+
),
267+
(
268+
False,
269+
True,
270+
True,
271+
[
272+
"group:moderators",
273+
"group:psf_staff",
274+
"group:with_admin_dashboard_access",
275+
],
276+
),
236277
],
237278
)
238-
def test_with_user(self, is_superuser, is_moderator, expected):
239-
user = pretend.stub(is_superuser=is_superuser, is_moderator=is_moderator)
279+
def test_with_user(self, is_superuser, is_moderator, is_psf_staff, expected):
280+
user = pretend.stub(
281+
is_superuser=is_superuser,
282+
is_moderator=is_moderator,
283+
is_psf_staff=is_psf_staff,
284+
)
240285
service = pretend.stub(get_user=pretend.call_recorder(lambda userid: user))
241286
request = pretend.stub(find_service=lambda iface, context: service)
242287

tests/unit/admin/test_routes.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,24 @@ def test_includeme():
160160
"/admin/verdicts/{verdict_id}/review",
161161
domain=warehouse,
162162
),
163+
pretend.call(
164+
"admin.sponsor.list",
165+
"/admin/sponsors/",
166+
domain=warehouse,
167+
),
168+
pretend.call(
169+
"admin.sponsor.create",
170+
"/admin/sponsors/create/",
171+
domain=warehouse,
172+
),
173+
pretend.call(
174+
"admin.sponsor.delete",
175+
"/admin/sponsors/{sponsor_id}/delete/",
176+
domain=warehouse,
177+
),
178+
pretend.call(
179+
"admin.sponsor.edit",
180+
"/admin/sponsors/{sponsor_id}/",
181+
domain=warehouse,
182+
),
163183
]

0 commit comments

Comments
 (0)