From bb0110353d10cc5fa5dcaa2d53feb975dd11c83d Mon Sep 17 00:00:00 2001 From: Nora Shapiro Date: Fri, 3 Jan 2025 17:33:29 -0800 Subject: [PATCH] change plan and member urls for gitlab users --- database/models/core.py | 13 ++++---- requirements.in | 2 +- requirements.txt | 2 +- services/tests/test_urls.py | 60 ++++++++++++++++++++++++++++++++++++- services/urls.py | 17 +++++++++-- 5 files changed, 84 insertions(+), 10 deletions(-) diff --git a/database/models/core.py b/database/models/core.py index 9dbc172bc..3fdfa2c89 100644 --- a/database/models/core.py +++ b/database/models/core.py @@ -3,6 +3,7 @@ import uuid from datetime import datetime from functools import cached_property +from typing import Optional from shared.plan.constants import PlanName from sqlalchemy import Column, ForeignKey, Index, UniqueConstraint, types @@ -154,11 +155,11 @@ class Owner(CodecovBaseModel): ) @property - def slug(self): + def slug(self: "Owner") -> str: return self.username @property - def root_organization(self): + def root_organization(self: "Owner") -> Optional["Owner"]: """ Find the root organization of Gitlab OwnerOrg, by using the root_parent_service_id if it exists, otherwise iterating through the parents and cache it in root_parent_service_id @@ -178,17 +179,19 @@ def root_organization(self): db_session.commit() return root - def _get_owner_by_service_id(self, db_session, service_id): + def _get_owner_by_service_id( + self: "Owner", db_session: Session, service_id: str + ) -> "Owner": """ Helper method to fetch an Owner by service_id. """ return ( db_session.query(Owner) .filter_by(service_id=service_id, service=self.service) - .one_or_none() + .one() ) - def __repr__(self): + def __repr__(self: "Owner") -> str: return f"Owner<{self.ownerid}@service<{self.service}>>" diff --git a/requirements.in b/requirements.in index bf9595933..fbd2157fd 100644 --- a/requirements.in +++ b/requirements.in @@ -1,5 +1,5 @@ https://github.com/codecov/test-results-parser/archive/996ecb2aaf7767bf4c2944c75835c1ee1eb2b566.tar.gz#egg=test-results-parser -https://github.com/codecov/shared/archive/b186b3c89fe16a4f9512cf160f39cfd262ba2eb8.tar.gz#egg=shared +https://github.com/codecov/shared/archive/9e55b8a802ed0d08064fa2dc4cbd6689a6b0805f.tar.gz#egg=shared https://github.com/codecov/timestring/archive/d37ceacc5954dff3b5bd2f887936a98a668dda42.tar.gz#egg=timestring asgiref>=3.7.2 analytics-python==1.3.0b1 diff --git a/requirements.txt b/requirements.txt index 77e3e98e4..501ae7d68 100644 --- a/requirements.txt +++ b/requirements.txt @@ -336,7 +336,7 @@ sentry-sdk==2.13.0 # shared setuptools==75.6.0 # via nodeenv -shared @ https://github.com/codecov/shared/archive/b186b3c89fe16a4f9512cf160f39cfd262ba2eb8.tar.gz#egg=shared +shared @ https://github.com/codecov/shared/archive/9e55b8a802ed0d08064fa2dc4cbd6689a6b0805f.tar.gz#egg=shared # via -r requirements.in six==1.16.0 # via diff --git a/services/tests/test_urls.py b/services/tests/test_urls.py index 144d1ea16..54ab2f1e9 100644 --- a/services/tests/test_urls.py +++ b/services/tests/test_urls.py @@ -1,4 +1,5 @@ -from services.urls import append_tracking_params_to_urls +from database.tests.factories import OwnerFactory, PullFactory, RepositoryFactory +from services.urls import append_tracking_params_to_urls, get_members_url, get_plan_url def test_append_tracking_params_to_urls(): @@ -29,3 +30,60 @@ def test_append_tracking_params_to_urls(): ] assert result == expected_result + + +class TestURLs(object): + def test_gitlab_url_username_swap(self, dbsession): + base_for_member_url = "https://app.codecov.io/members/" + base_for_plan_url = "https://app.codecov.io/plan/" + + github_org = OwnerFactory.create( + service="github", + username="gh", + ) + dbsession.add(github_org) + r = RepositoryFactory.create(owner=github_org) + dbsession.add(r) + gh_pull = PullFactory.create(repository=r) + dbsession.add(gh_pull) + dbsession.flush() + member_url = get_members_url(gh_pull) + assert member_url == base_for_member_url + "gh/gh" + + gitlab_root_org = OwnerFactory.create(service="gitlab", username="gl_root") + dbsession.add(gitlab_root_org) + r = RepositoryFactory.create(owner=gitlab_root_org) + dbsession.add(r) + gl_root_pull = PullFactory.create(repository=r) + dbsession.add(gl_root_pull) + dbsession.flush() + plan_url = get_plan_url(gl_root_pull) + assert plan_url == base_for_plan_url + "gl/gl_root" + + gitlab_mid_org = OwnerFactory.create( + service="gitlab", + username="gl_mid", + parent_service_id=gitlab_root_org.service_id, + ) + dbsession.add(gitlab_mid_org) + r = RepositoryFactory.create(owner=gitlab_mid_org) + dbsession.add(r) + gl_mid_pull = PullFactory.create(repository=r) + dbsession.add(gl_mid_pull) + dbsession.flush() + member_url = get_members_url(gl_mid_pull) + assert member_url == base_for_member_url + "gl/gl_root" + + gitlab_sub_org = OwnerFactory.create( + service="gitlab", + username="gl_child", + parent_service_id=gitlab_mid_org.service_id, + ) + dbsession.add(gitlab_sub_org) + r = RepositoryFactory.create(owner=gitlab_sub_org) + dbsession.add(r) + gl_child_pull = PullFactory.create(repository=r) + dbsession.add(gl_child_pull) + dbsession.flush() + plan_url = get_plan_url(gl_child_pull) + assert plan_url == base_for_plan_url + "gl/gl_root" diff --git a/services/urls.py b/services/urls.py index 0122d209b..ad7a625d2 100644 --- a/services/urls.py +++ b/services/urls.py @@ -6,6 +6,7 @@ from urllib.parse import parse_qs, quote_plus, urlencode, urlparse, urlunparse from shared.config import get_config +from shared.django_apps.codecov_auth.models import Service from database.models import Commit, Pull, Repository from services.license import requires_license @@ -57,6 +58,16 @@ def get_dashboard_base_url() -> str: return configured_dashboard_url or "https://app.codecov.io" +def _get_username_for_url(repository: Repository) -> str: + username = repository.owner.username + if repository.owner.service == Service.GITLAB.value: + # if GL, direct url to root org not subgroup + root_org = repository.owner.root_organization + if root_org is not None: + username = root_org.username + return username + + def get_commit_url(commit: Commit) -> str: return SiteUrls.commit_url.get_url( base_url=get_dashboard_base_url(), @@ -162,11 +173,12 @@ def get_org_account_url(pull: Pull) -> str: def get_members_url(pull: Pull) -> str: repository = pull.repository + username = _get_username_for_url(repository=repository) if not requires_license(): return SiteUrls.members_url.get_url( dashboard_base_url=get_dashboard_base_url(), service_short=services_short_dict.get(repository.service), - username=repository.owner.username, + username=username, ) else: return SiteUrls.members_url_self_hosted.get_url( @@ -178,10 +190,11 @@ def get_members_url(pull: Pull) -> str: def get_plan_url(pull: Pull) -> str: repository = pull.repository + username = _get_username_for_url(repository=repository) return SiteUrls.plan_url.get_url( dashboard_base_url=get_dashboard_base_url(), service_short=services_short_dict.get(repository.service), - username=repository.owner.username, + username=username, )