Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Alternate Repository Location for PEP 708 #15716

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
04e9ec1
initial attempt at adding alternate repository location details
cofiem Apr 3, 2024
837e464
Merge branch 'pypi:main' into feature/add-project-alternate-repositor…
cofiem Apr 14, 2024
401ed2b
Merge branch 'refs/heads/main' into feature/add-project-alternate-rep…
cofiem Apr 24, 2024
6877707
implement per-project alternate locations metadata
cofiem Apr 25, 2024
81cdf21
starting to add tests
cofiem Apr 25, 2024
cf94d59
Merge branch 'refs/heads/main' into feature/add-project-alternate-rep…
cofiem May 5, 2024
6759b55
Merge branch 'refs/heads/main' into feature/add-project-alternate-rep…
cofiem Jun 29, 2024
f21d788
starting to add tests
cofiem Jun 29, 2024
8d53891
Merge branch 'refs/heads/main' into feature/add-project-alternate-rep…
cofiem Jun 30, 2024
cc9111b
added tests
cofiem Jun 30, 2024
3517006
updated translations
cofiem Jun 30, 2024
5323726
satisfy test coverage
cofiem Jun 30, 2024
bfab8e3
update translations
cofiem Jun 30, 2024
ef9d6db
Merge remote-tracking branch 'origin/feature/add-project-alternate-re…
cofiem Jul 13, 2024
987d129
Merge branch 'refs/heads/main' into feature/add-project-alternate-rep…
cofiem Jul 13, 2024
e6fdd04
update translations
cofiem Jul 13, 2024
52542f9
register cache and purge keys for AlternateRepository objects
cofiem Jul 13, 2024
e52f44e
change db migration down revision to most recent migration
cofiem Jul 13, 2024
69c6362
update test after adding alternate repository cache and purge key
cofiem Jul 13, 2024
95ee51a
Merge branch 'refs/heads/main' into feature/add-project-alternate-rep…
cofiem Jul 21, 2024
60970a6
Merge branch 'refs/heads/main' into feature/add-project-alternate-rep…
cofiem Jul 27, 2024
1849468
Merge branch 'refs/heads/main' into feature/add-project-alternate-rep…
cofiem Aug 23, 2024
6d4a484
increment api version to 1.2
cofiem Aug 23, 2024
2316455
add url the response was fetched from
cofiem Aug 23, 2024
f7f9244
change db migration down revision to most recent migration
cofiem Aug 23, 2024
1df1394
name is already normalized
cofiem Aug 23, 2024
415b85f
update translations
cofiem Aug 23, 2024
64fa33b
match functionality between JSON and HTML simple API
ewdurbin Aug 23, 2024
21ddc57
Merge branch 'main' into feature/add-project-alternate-repository-loc…
ewdurbin Sep 6, 2024
724fc0f
update migration
ewdurbin Sep 6, 2024
4e9e773
Merge branch 'main' into feature/add-project-alternate-repository-loc…
ewdurbin Sep 16, 2024
3fffca8
Merge branch 'main' into feature/add-project-alternate-repository-loc…
ewdurbin Sep 19, 2024
ebe29e8
remove self-reference from Simple HTML and JSON
ewdurbin Sep 19, 2024
03c15ce
add a callout in project management settings around Alternate Locations
ewdurbin Sep 19, 2024
68afe18
Merge branch 'main' into feature/add-project-alternate-repository-loc…
ewdurbin Sep 19, 2024
d1ac800
translations
ewdurbin Sep 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions tests/common/db/packaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

from warehouse.observations.models import ObservationKind
from warehouse.packaging.models import (
AlternateRepository,
Dependency,
DependencyKind,
Description,
Expand Down Expand Up @@ -200,3 +201,13 @@ class Meta:
)
name = factory.Faker("pystr", max_chars=12)
prohibited_by = factory.SubFactory(UserFactory)


class AlternateRepositoryFactory(WarehouseFactory):
class Meta:
model = AlternateRepository

name = factory.Faker("word")
url = factory.Faker("uri")
description = factory.Faker("text")
project = factory.SubFactory(ProjectFactory)
72 changes: 51 additions & 21 deletions tests/unit/api/test_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
from pyramid.testing import DummyRequest

from warehouse.api import simple
from warehouse.packaging.utils import API_VERSION
from warehouse.packaging.utils import API_VERSION, _valid_simple_detail_context
ewdurbin marked this conversation as resolved.
Show resolved Hide resolved

from ...common.db.accounts import UserFactory
from ...common.db.packaging import (
AlternateRepositoryFactory,
FileFactory,
JournalEntryFactory,
ProjectFactory,
Expand All @@ -48,29 +49,30 @@ def test_defaults_text_html(self, header):
default to text/html.
"""
request = DummyRequest(accept=header)
assert simple._select_content_type(request) == "text/html"
assert simple._select_content_type(request) == simple.MIME_TEXT_HTML

@pytest.mark.parametrize(
"header, expected",
[
("text/html", "text/html"),
(simple.MIME_TEXT_HTML, simple.MIME_TEXT_HTML),
(
"application/vnd.pypi.simple.v1+html",
"application/vnd.pypi.simple.v1+html",
simple.MIME_PYPI_SIMPLE_V1_HTML,
simple.MIME_PYPI_SIMPLE_V1_HTML,
),
(
"application/vnd.pypi.simple.v1+json",
"application/vnd.pypi.simple.v1+json",
simple.MIME_PYPI_SIMPLE_V1_JSON,
simple.MIME_PYPI_SIMPLE_V1_JSON,
),
(
"text/html, application/vnd.pypi.simple.v1+html, "
"application/vnd.pypi.simple.v1+json",
"text/html",
f"{simple.MIME_TEXT_HTML}, {simple.MIME_PYPI_SIMPLE_V1_HTML}, "
f"{simple.MIME_PYPI_SIMPLE_V1_JSON}",
simple.MIME_TEXT_HTML,
),
(
"text/html;q=0.01, application/vnd.pypi.simple.v1+html;q=0.2, "
"application/vnd.pypi.simple.v1+json",
"application/vnd.pypi.simple.v1+json",
f"{simple.MIME_TEXT_HTML};q=0.01, "
f"{simple.MIME_PYPI_SIMPLE_V1_HTML};q=0.2, "
f"{simple.MIME_PYPI_SIMPLE_V1_JSON}",
simple.MIME_PYPI_SIMPLE_V1_JSON,
),
],
)
Expand All @@ -80,9 +82,9 @@ def test_selects(self, header, expected):


CONTENT_TYPE_PARAMS = [
("text/html", None),
("application/vnd.pypi.simple.v1+html", None),
("application/vnd.pypi.simple.v1+json", "json"),
(simple.MIME_TEXT_HTML, None),
(simple.MIME_PYPI_SIMPLE_V1_HTML, None),
(simple.MIME_PYPI_SIMPLE_V1_JSON, "json"),
]


Expand Down Expand Up @@ -198,12 +200,15 @@ def test_no_files_no_serial(self, db_request, content_type, renderer_override):
user = UserFactory.create()
JournalEntryFactory.create(submitted_by=user)

assert simple.simple_detail(project, db_request) == {
context = {
"meta": {"_last-serial": 0, "api-version": API_VERSION},
"name": project.normalized_name,
"files": [],
"versions": [],
"alternate-locations": [],
}
context = _update_context(context, content_type, renderer_override)
assert simple.simple_detail(project, db_request) == context

assert db_request.response.headers["X-PyPI-Last-Serial"] == "0"
assert db_request.response.content_type == content_type
Expand All @@ -222,13 +227,20 @@ def test_no_files_with_serial(self, db_request, content_type, renderer_override)
db_request.matchdict["name"] = project.normalized_name
user = UserFactory.create()
je = JournalEntryFactory.create(name=project.name, submitted_by=user)
als = [
AlternateRepositoryFactory.create(project=project),
AlternateRepositoryFactory.create(project=project),
]

assert simple.simple_detail(project, db_request) == {
context = {
"meta": {"_last-serial": je.id, "api-version": API_VERSION},
"name": project.normalized_name,
"files": [],
"versions": [],
"alternate-locations": sorted(al.url for al in als),
}
context = _update_context(context, content_type, renderer_override)
assert simple.simple_detail(project, db_request) == context

assert db_request.response.headers["X-PyPI-Last-Serial"] == str(je.id)
assert db_request.response.content_type == content_type
Expand Down Expand Up @@ -258,7 +270,7 @@ def test_with_files_no_serial(self, db_request, content_type, renderer_override)
user = UserFactory.create()
JournalEntryFactory.create(submitted_by=user)

assert simple.simple_detail(project, db_request) == {
context = {
"meta": {"_last-serial": 0, "api-version": API_VERSION},
"name": project.normalized_name,
"versions": release_versions,
Expand All @@ -276,7 +288,10 @@ def test_with_files_no_serial(self, db_request, content_type, renderer_override)
}
for f in files
],
"alternate-locations": [],
}
context = _update_context(context, content_type, renderer_override)
assert simple.simple_detail(project, db_request) == context

assert db_request.response.headers["X-PyPI-Last-Serial"] == "0"
assert db_request.response.content_type == content_type
Expand Down Expand Up @@ -306,7 +321,7 @@ def test_with_files_with_serial(self, db_request, content_type, renderer_overrid
user = UserFactory.create()
je = JournalEntryFactory.create(name=project.name, submitted_by=user)

assert simple.simple_detail(project, db_request) == {
context = {
"meta": {"_last-serial": je.id, "api-version": API_VERSION},
"name": project.normalized_name,
"versions": release_versions,
Expand All @@ -324,7 +339,10 @@ def test_with_files_with_serial(self, db_request, content_type, renderer_overrid
}
for f in files
],
"alternate-locations": [],
}
context = _update_context(context, content_type, renderer_override)
assert simple.simple_detail(project, db_request) == context

assert db_request.response.headers["X-PyPI-Last-Serial"] == str(je.id)
assert db_request.response.content_type == content_type
Expand Down Expand Up @@ -391,7 +409,7 @@ def test_with_files_with_version_multi_digit(
user = UserFactory.create()
je = JournalEntryFactory.create(name=project.name, submitted_by=user)

assert simple.simple_detail(project, db_request) == {
context = {
"meta": {"_last-serial": je.id, "api-version": API_VERSION},
"name": project.normalized_name,
"versions": release_versions,
Expand All @@ -417,11 +435,23 @@ def test_with_files_with_version_multi_digit(
}
for f in files
],
"alternate-locations": [],
}
context = _update_context(context, content_type, renderer_override)
assert simple.simple_detail(project, db_request) == context

assert db_request.response.headers["X-PyPI-Last-Serial"] == str(je.id)
assert db_request.response.content_type == content_type
_assert_has_cors_headers(db_request.response.headers)

if renderer_override is not None:
assert db_request.override_renderer == renderer_override


def _update_context(context, content_type, renderer_override):
if renderer_override != "json" or content_type in [
simple.MIME_TEXT_HTML,
simple.MIME_PYPI_SIMPLE_V1_HTML,
]:
return _valid_simple_detail_context(context)
return context
Comment on lines +464 to +470
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing something, but I think this helper is currently causing the test_with_files_quarantined_omitted_from_index test to be skipped (since it's an instance method, but it's currently indented into the helper.) I'll send a PR to fix it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #16777 with the fix.

Loading