diff --git a/assets/src/bundles/browse/browse-utils.js b/assets/src/bundles/browse/browse-utils.js --- a/assets/src/bundles/browse/browse-utils.js +++ b/assets/src/bundles/browse/browse-utils.js @@ -87,7 +87,8 @@ window.location.pathname.endsWith('branches/')) { $('#swh-browse-snapshot-branches-nav-link').addClass('active'); } else if (window.location.pathname === Urls.browse_origin_releases() || - window.location.pathname === Urls.browse_snapshot_releases()) { + window.location.pathname === Urls.browse_snapshot_releases() || + window.location.pathname.endsWith('releases/')) { $('#swh-browse-snapshot-releases-nav-link').addClass('active'); } else { $('#swh-browse-code-nav-link').addClass('active'); diff --git a/cypress/integration/origin-browse.spec.js b/cypress/integration/origin-browse.spec.js --- a/cypress/integration/origin-browse.spec.js +++ b/cypress/integration/origin-browse.spec.js @@ -35,7 +35,7 @@ .click(); cy.location('pathname') - .should('eq', this.Urls.browse_origin_releases()); + .should('eq', this.Urls.browse_snapshot_releases(this.origin[1].snapshot)); cy.location('search') .should('eq', `?origin_url=${this.origin[1].url}`); diff --git a/docs/uri-scheme-browse-origin.rst b/docs/uri-scheme-browse-origin.rst --- a/docs/uri-scheme-browse-origin.rst +++ b/docs/uri-scheme-browse-origin.rst @@ -589,7 +589,7 @@ :deprecated: .. warning:: - That endpoint is deprecated, use :http:get:`/browse/origin/log/` instead. + That endpoint is deprecated, use :http:get:`/browse/snapshot/log/` instead. HTML view that produces a display of revisions history heading to the last revision found during a visit of a software origin closest @@ -721,7 +721,7 @@ :deprecated: .. warning:: - That endpoint is deprecated, use :http:get:`/browse/origin/branches/` instead. + That endpoint is deprecated, use :http:get:`/browse/snapshot/branches/` instead. HTML view that produces a display of the list of branches found during a visit of a software origin closest to the provided timestamp. @@ -753,6 +753,10 @@ """"""""""""""" .. http:get:: /browse/origin/releases/ + :deprecated: + + .. warning:: + That endpoint is deprecated, use :http:get:`/browse/snapshot/releases/` instead. HTML view that produces a display of the list of releases found during the latest full visit of a software origin. @@ -789,7 +793,7 @@ :deprecated: .. warning:: - That endpoint is deprecated, use :http:get:`/browse/origin/releases/` instead. + That endpoint is deprecated, use :http:get:`/browse/snapshot/releases/` instead. HTML view that produces a display of the list of releases found during the latest full visit of a software origin. @@ -824,7 +828,7 @@ :deprecated: .. warning:: - That endpoint is deprecated, use :http:get:`/browse/origin/releases/` instead. + That endpoint is deprecated, use :http:get:`/browse/snapshot/releases/` instead. HTML view that produces a display of the list of releases found during a visit of a software origin closest to the provided timestamp. diff --git a/docs/uri-scheme-browse-snapshot.rst b/docs/uri-scheme-browse-snapshot.rst --- a/docs/uri-scheme-browse-snapshot.rst +++ b/docs/uri-scheme-browse-snapshot.rst @@ -135,7 +135,7 @@ :swh_web_browse:`snapshot/baebc2109e4a2ec22a1129a3859647e191d04df4/content/?path=init/initramfs.c` :swh_web_browse:`snapshot/673156c31a876c5b99b2fe3e89615529de9a3c44/content/?path=src/opengl/qglbuffer.h` - :swh_web_browse:`snapshot/673156c31a876c5b99b2fe3e89615529de9a3c44/content/?path=src/opengl/qglbuffer.h&?release=v5.0.0` + :swh_web_browse:`snapshot/673156c31a876c5b99b2fe3e89615529de9a3c44/content/?path=src/opengl/qglbuffer.h&release=v5.0.0` .. http:get:: /browse/snapshot/(snapshot_id)/content/(path)/ @@ -180,7 +180,7 @@ .. http:get:: /browse/snapshot/log/ HTML view that produces a display of the revisions history (aka the commit log) - for the last collected revision for the given origin. An origin URL must be given + for the last collected revision for the given origin. An origin URL must be provided as a query parameter. :query string origin_url: specify the origin from which to retrieve the commit log @@ -188,7 +188,7 @@ find the closest visit :statuscode 200: no error - :statuscode 400: origin_url parameter is missing + :statuscode 400: origin_url parameter is missing or an invalid snapshot identifier has been provided :statuscode 404: requested origin is either missing or has no associated snapshots **Examples:** @@ -235,7 +235,7 @@ .. http:get:: /browse/snapshot/branches/ HTML view that produces a display of the list of branches collected in a snapshot. - An origin URL must be given as a query parameter. + An origin URL must be provided as a query parameter. :query string origin_url: specify the origin from which to retrieve the snapshot and branches :query string timestamp: optional; an ISO 8601 datetime string to parse in order to @@ -243,7 +243,7 @@ :name_include: optional; to filter branches by name :statuscode 200: no error - :statuscode 400: origin_url parameter is missing + :statuscode 400: origin_url parameter is missing or an invalid snapshot identifier has been provided :statuscode 404: requested origin is either missing or has no associated snapshots **Examples:** @@ -254,7 +254,6 @@ :swh_web_browse:`snapshot/branches/?origin_url=https://github.com/python/cpython×tamp=2021-01-23T22:24:10Z` :swh_web_browse:`snapshot/branches/?origin_url=https://github.com/python/cpython&name_include=v2` - .. http:get:: /browse/snapshot/(snapshot_id)/branches/ HTML view that produces a display of the list of branches @@ -280,6 +279,28 @@ Snapshot releases """"""""""""""""" +.. http:get:: /browse/snapshot/releases/ + + HTML view that produces a display of the list of releases + collected in a snapshot. An origin URL must be provided as a query parameter. + + :query string origin_url: specify the origin from which to retrieve the snapshot and branches + :query string timestamp: optional; an ISO 8601 datetime string to parse in order to + find the closest visit + :name_include: optional; to filter releases by name + + :statuscode 200: no error + :statuscode 400: origin_url parameter is missing or an invalid snapshot identifier has been provided + :statuscode 404: requested origin is either missing or has no associated snapshots + + **Examples:** + + .. parsed-literal:: + + :swh_web_browse:`snapshot/releases?origin_url=https://github.com/python/cpython` + :swh_web_browse:`snapshot/releases/?origin_url=https://github.com/python/cpython×tamp=2021-01-23T22:24:10Z` + :swh_web_browse:`snapshot/releases/?origin_url=https://github.com/python/cpython&name_include=v2` + .. http:get:: /browse/snapshot/(snapshot_id)/releases/ HTML view that produces a display of the list of releases diff --git a/swh/web/browse/views/origin.py b/swh/web/browse/views/origin.py --- a/swh/web/browse/views/origin.py +++ b/swh/web/browse/views/origin.py @@ -10,7 +10,6 @@ from swh.web.browse.snapshot_context import ( browse_snapshot_content, browse_snapshot_directory, - browse_snapshot_releases, get_snapshot_context, ) from swh.web.common import archive @@ -190,19 +189,16 @@ r"origin/releases/", view_name="browse-origin-releases", ) def origin_releases_browse(request): - """Django view that produces an HTML display of the list of releases + """ + This route is deprecated; use http:get:`/browse/snapshot/releases` instead + + Django view that produces an HTML display of the list of releases associated to an origin for a given visit. The URL that points to it is :http:get:`/browse/origin/releases/` """ - return browse_snapshot_releases( - request, - origin_url=request.GET.get("origin_url"), - snapshot_id=request.GET.get("snapshot"), - timestamp=request.GET.get("timestamp"), - release_name_include=request.GET.get("name_include"), - ) + return redirect_to_new_route(request, "browse-snapshot-releases") @browse_route( @@ -211,7 +207,10 @@ view_name="browse-origin-releases-legacy", ) def origin_releases_browse_legacy(request, origin_url, timestamp=None): - """Django view that produces an HTML display of the list of releases + """ + This route is deprecated; use http:get:`/browse/snapshot/releases` instead + + Django view that produces an HTML display of the list of releases associated to an origin for a given visit. The URLs that point to it are @@ -219,12 +218,7 @@ :http:get:`/browse/origin/(origin_url)/visit/(timestamp)/releases/` """ - return browse_snapshot_releases( - request, - origin_url=origin_url, - snapshot_id=request.GET.get("snapshot"), - timestamp=timestamp, - ) + return redirect_to_new_route(request, "browse-snapshot-releases") def _origin_visits_browse(request, origin_url): diff --git a/swh/web/browse/views/snapshot.py b/swh/web/browse/views/snapshot.py --- a/swh/web/browse/views/snapshot.py +++ b/swh/web/browse/views/snapshot.py @@ -196,14 +196,34 @@ @browse_route( r"snapshot/(?P[0-9a-f]+)/releases/", + r"snapshot/releases/", view_name="browse-snapshot-releases", checksum_args=["snapshot_id"], ) -def snapshot_releases_browse(request, snapshot_id): +def snapshot_releases_browse(request, snapshot_id=None): """Django view that produces an HTML display of the list of releases collected in a snapshot. - The url that points to it is + The URLs that point to it are :http:get:`/browse/snapshot/(snapshot_id)/releases/` + :http:get:`/browse/snapshot/releases/` """ - return browse_snapshot_releases(request, snapshot_id=snapshot_id) + if snapshot_id is None: + # This case happens when redirected from /origin/releases + snapshot_id = get_snapshot_from_request(request) + # Redirect to the same route with the newest snapshot_id + # for the given origin + return redirect( + reverse( + "browse-snapshot-releases", + url_args={"snapshot_id": snapshot_id}, + query_params=request.GET, + ), + ) + return browse_snapshot_releases( + request, + snapshot_id=snapshot_id, + origin_url=request.GET.get("origin_url"), + timestamp=request.GET.get("timestamp"), + release_name_include=request.GET.get("name_include"), + ) diff --git a/swh/web/tests/browse/views/test_origin.py b/swh/web/tests/browse/views/test_origin.py --- a/swh/web/tests/browse/views/test_origin.py +++ b/swh/web/tests/browse/views/test_origin.py @@ -395,21 +395,6 @@ ) -def test_origin_releases(client, archive_data, origin): - origin_visits = archive_data.origin_visit_get(origin["url"]) - - visit = origin_visits[-1] - snapshot = archive_data.snapshot_get(visit["snapshot"]) - snapshot_sizes = archive_data.snapshot_count_branches(snapshot["id"]) - snapshot_content = process_snapshot_branches(snapshot) - - _origin_releases_test_helper(client, origin, snapshot_content, snapshot_sizes) - - _origin_releases_test_helper( - client, origin, snapshot_content, snapshot_sizes, snapshot_id=visit["snapshot"] - ) - - @given( new_origin(), new_snapshot(min_size=4, max_size=4), visit_dates(), ) @@ -816,7 +801,6 @@ for browse_context in ( "content", "directory", - "releases", "visits", ): url = reverse(f"browse-origin-{browse_context}") @@ -829,7 +813,7 @@ @given(new_origin()) -@pytest.mark.parametrize("browse_context", ["log", "branches"]) +@pytest.mark.parametrize("browse_context", ["log", "branches", "releases"]) def test_origin_view_redirects(client, browse_context, new_origin): query_params = {"origin_url": new_origin.url} url = reverse(f"browse-origin-{browse_context}", query_params=query_params) @@ -841,7 +825,7 @@ @given(new_origin()) -@pytest.mark.parametrize("browse_context", ["log", "branches"]) +@pytest.mark.parametrize("browse_context", ["log", "branches", "releases"]) def test_origin_view_legacy_redirects(client, browse_context, new_origin): params = {"origin_url": new_origin.url, "timestamp": "2021-01-23T22:24:10Z"} url = reverse( @@ -1138,52 +1122,6 @@ assert_not_contains(resp, "swh-metadata-popover") -def _origin_releases_test_helper( - client, origin_info, origin_snapshot, snapshot_sizes, snapshot_id=None -): - query_params = {"origin_url": origin_info["url"], "snapshot": snapshot_id} - - url = reverse("browse-origin-releases", query_params=query_params) - - resp = check_html_get_response( - client, url, status_code=200, template_used="browse/releases.html" - ) - - origin_releases = origin_snapshot[1] - - origin_branches_url = reverse("browse-origin-branches", query_params=query_params) - - assert_contains(resp, f'href="{escape(origin_branches_url)}"') - assert_contains(resp, f"Branches ({snapshot_sizes['revision']})") - - origin_releases_url = reverse("browse-origin-releases", query_params=query_params) - - nb_releases = len(origin_releases) - if nb_releases > 0: - assert_contains(resp, f'href="{escape(origin_releases_url)}"') - assert_contains(resp, f"Releases ({snapshot_sizes['release']}") - - assert_contains(resp, '' % escape(browse_release_url)) - assert_contains(resp, '' % escape(browse_revision_url)) - - _check_origin_link(resp, origin_info["url"]) - - def _check_origin_link(resp, origin_url): browse_origin_url = reverse( "browse-origin", query_params={"origin_url": origin_url} diff --git a/swh/web/tests/browse/views/test_snapshot.py b/swh/web/tests/browse/views/test_snapshot.py --- a/swh/web/tests/browse/views/test_snapshot.py +++ b/swh/web/tests/browse/views/test_snapshot.py @@ -26,7 +26,11 @@ @pytest.mark.parametrize( "browse_context,template_used", - [("log", "revision-log.html"), ("branches", "branches.html")], + [ + ("log", "revision-log.html"), + ("branches", "branches.html"), + ("releases", "releases.html"), + ], ) def test_snapshot_browse_with_id(client, browse_context, template_used, snapshot): url = reverse( @@ -39,7 +43,7 @@ assert_contains(resp, f"swh:1:snp:{snapshot}") -@pytest.mark.parametrize("browse_context", ["log", "branches"]) +@pytest.mark.parametrize("browse_context", ["log", "branches", "releases"]) def test_snapshot_browse_with_id_and_origin( client, browse_context, archive_data, origin ): @@ -56,7 +60,7 @@ assert_contains(resp, origin["url"]) -@pytest.mark.parametrize("browse_context", ["log", "branches"]) +@pytest.mark.parametrize("browse_context", ["log", "branches", "releases"]) def test_snapshot_browse_with_id_origin_and_timestamp( client, browse_context, archive_data, origin_with_multiple_visits ): @@ -74,7 +78,7 @@ assert_contains(resp, visit["origin"]) -@pytest.mark.parametrize("browse_context", ["log", "branches"]) +@pytest.mark.parametrize("browse_context", ["log", "branches", "releases"]) def test_snapshot_browse_without_id(client, browse_context, archive_data, origin): url = reverse( f"browse-snapshot-{browse_context}", query_params={"origin_url": origin["url"]} @@ -90,7 +94,7 @@ ) -@pytest.mark.parametrize("browse_context", ["log", "branches"]) +@pytest.mark.parametrize("browse_context", ["log", "branches", "releases"]) def test_snapshot_browse_without_id_and_origin(client, browse_context): url = reverse(f"browse-snapshot-{browse_context}") resp = check_html_get_response(client, url, status_code=400,) @@ -243,3 +247,66 @@ client, url, status_code=200, template_used="browse/branches.html" ) assert_not_contains(resp, "refs/pull/") + + +def test_snapshot_browse_releases(client, archive_data, origin): + origin_visits = archive_data.origin_visit_get(origin["url"]) + + visit = origin_visits[-1] + snapshot = archive_data.snapshot_get(visit["snapshot"]) + snapshot_sizes = archive_data.snapshot_count_branches(snapshot["id"]) + snapshot_content = process_snapshot_branches(snapshot) + + _origin_releases_test_helper( + client, origin, snapshot_content, snapshot_sizes, snapshot_id=visit["snapshot"] + ) + + +def _origin_releases_test_helper( + client, origin_info, origin_snapshot, snapshot_sizes, snapshot_id=None +): + query_params = {"origin_url": origin_info["url"], "snapshot": snapshot_id} + + url = reverse( + "browse-snapshot-releases", + url_args={"snapshot_id": snapshot_id}, + query_params=query_params, + ) + + resp = check_html_get_response( + client, url, status_code=200, template_used="browse/releases.html" + ) + + origin_releases = origin_snapshot[1] + + origin_branches_url = reverse("browse-origin-branches", query_params=query_params) + + assert_contains(resp, f'href="{escape(origin_branches_url)}"') + assert_contains(resp, f"Branches ({snapshot_sizes['revision']})") + + origin_releases_url = reverse("browse-origin-releases", query_params=query_params) + + nb_releases = len(origin_releases) + if nb_releases > 0: + assert_contains(resp, f'href="{escape(origin_releases_url)}"') + assert_contains(resp, f"Releases ({snapshot_sizes['release']}") + + assert_contains(resp, '' % escape(browse_release_url)) + assert_contains(resp, '' % escape(browse_revision_url)) + + _check_origin_link(resp, origin_info["url"])