diff --git a/api/public/v2/component/views.py b/api/public/v2/component/views.py index 02c5cff30a..4e50de67da 100644 --- a/api/public/v2/component/views.py +++ b/api/public/v2/component/views.py @@ -1,6 +1,9 @@ +from typing import Any + from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiParameter, extend_schema from rest_framework import viewsets +from rest_framework.request import Request from rest_framework.response import Response from api.public.v2.component.serializers import ComponentSerializer @@ -34,7 +37,7 @@ class ComponentViewSet(viewsets.ViewSet, RepoPropertyMixin): permission_classes = [RepositoryArtifactPermissions] @extend_schema(summary="Component list") - def list(self, request, *args, **kwargs): + def list(self, request: Request, *args: Any, **kwargs: Any) -> Response: """ Returns a list of components for the specified repository """ diff --git a/billing/views.py b/billing/views.py index ec1de279c1..9094e97959 100644 --- a/billing/views.py +++ b/billing/views.py @@ -1,6 +1,6 @@ import logging from datetime import datetime -from typing import List +from typing import Any, List import stripe from django.conf import settings @@ -410,7 +410,7 @@ def checkout_session_completed( self._log_updated([owner]) - def post(self, request: HttpRequest, *args, **kwargs) -> Response: + def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> Response: if settings.STRIPE_ENDPOINT_SECRET is None: log.critical( "Stripe endpoint secret improperly configured -- webhooks will not be processed." diff --git a/codecov_auth/authentication/repo_auth.py b/codecov_auth/authentication/repo_auth.py index 3258fd33ec..21e1c07074 100644 --- a/codecov_auth/authentication/repo_auth.py +++ b/codecov_auth/authentication/repo_auth.py @@ -1,6 +1,6 @@ import json import logging -from typing import List +from typing import Any, Dict, List, Optional, Tuple from uuid import UUID from django.core.exceptions import ObjectDoesNotExist @@ -10,6 +10,7 @@ from jwt import PyJWTError from rest_framework import authentication, exceptions, serializers from rest_framework.exceptions import NotAuthenticated +from rest_framework.response import Response from rest_framework.views import exception_handler from shared.django_apps.codecov_auth.models import Owner @@ -32,7 +33,9 @@ log = logging.getLogger(__name__) -def repo_auth_custom_exception_handler(exc, context): +def repo_auth_custom_exception_handler( + exc: Exception, context: Dict[str, Any] +) -> Response: """ User arrives here if they have correctly supplied a Token or the Tokenless Headers, but their Token has not matched with any of our Authentication methods. The goal is to @@ -60,17 +63,17 @@ def repo_auth_custom_exception_handler(exc, context): class LegacyTokenRepositoryAuth(RepositoryAuthInterface): - def __init__(self, repository, auth_data): + def __init__(self, repository: Repository, auth_data: Dict[str, Any]) -> None: self._auth_data = auth_data self._repository = repository - def get_scopes(self): + def get_scopes(self) -> List[TokenTypeChoices]: return [TokenTypeChoices.UPLOAD] - def get_repositories(self): + def get_repositories(self) -> List[Repository]: return [self._repository] - def allows_repo(self, repository): + def allows_repo(self, repository: Repository) -> bool: return repository in self.get_repositories() @@ -79,17 +82,17 @@ class OIDCTokenRepositoryAuth(LegacyTokenRepositoryAuth): class TableTokenRepositoryAuth(RepositoryAuthInterface): - def __init__(self, repository, token): + def __init__(self, repository: Repository, token: RepositoryToken) -> None: self._token = token self._repository = repository - def get_scopes(self): + def get_scopes(self) -> List[str]: return [self._token.token_type] - def get_repositories(self): + def get_repositories(self) -> List[Repository]: return [self._repository] - def allows_repo(self, repository): + def allows_repo(self, repository: Repository) -> bool: return repository in self.get_repositories() @@ -98,10 +101,10 @@ def __init__(self, token: OrganizationLevelToken) -> None: self._token = token self._org = token.owner - def get_scopes(self): + def get_scopes(self) -> List[str]: return [self._token.token_type] - def allows_repo(self, repository): + def allows_repo(self, repository: Repository) -> bool: return repository.author.ownerid == self._org.ownerid def get_repositories_queryset(self) -> QuerySet: @@ -120,10 +123,10 @@ class TokenlessAuth(RepositoryAuthInterface): def __init__(self, repository: Repository) -> None: self._repository = repository - def get_scopes(self): + def get_scopes(self) -> List[TokenTypeChoices]: return [TokenTypeChoices.UPLOAD] - def allows_repo(self, repository): + def allows_repo(self, repository: Repository) -> bool: return repository in self.get_repositories() def get_repositories(self) -> List[Repository]: @@ -131,7 +134,9 @@ def get_repositories(self) -> List[Repository]: class RepositoryLegacyQueryTokenAuthentication(authentication.BaseAuthentication): - def authenticate(self, request): + def authenticate( + self, request: HttpRequest + ) -> Optional[Tuple[RepositoryAsUser, LegacyTokenRepositoryAuth]]: token = request.GET.get("token") if not token: return None @@ -150,22 +155,26 @@ def authenticate(self, request): class RepositoryLegacyTokenAuthentication(authentication.TokenAuthentication): - def authenticate_credentials(self, token): + def authenticate_credentials( + self, token: str + ) -> Optional[Tuple[RepositoryAsUser, LegacyTokenRepositoryAuth]]: try: - token = UUID(token) - repository = Repository.objects.get(upload_token=token) + token_uuid = UUID(token) + repository = Repository.objects.get(upload_token=token_uuid) except (ValueError, TypeError, Repository.DoesNotExist): return None # continue to next auth class return ( RepositoryAsUser(repository), - LegacyTokenRepositoryAuth(repository, {"token": token}), + LegacyTokenRepositoryAuth(repository, {"token": token_uuid}), ) class RepositoryTokenAuthentication(authentication.TokenAuthentication): keyword = "Repotoken" - def authenticate_credentials(self, key): + def authenticate_credentials( + self, key: str + ) -> Optional[Tuple[RepositoryAsUser, TableTokenRepositoryAuth]]: try: token = RepositoryToken.objects.select_related("repository").get(key=key) except RepositoryToken.DoesNotExist: @@ -182,7 +191,9 @@ def authenticate_credentials(self, key): class GlobalTokenAuthentication(authentication.TokenAuthentication): - def authenticate(self, request): + def authenticate( + self, request: HttpRequest + ) -> Optional[Tuple[RepositoryAsUser, LegacyTokenRepositoryAuth]]: global_tokens = get_global_tokens() token = self.get_token(request) using_global_token = token in global_tokens @@ -219,7 +230,9 @@ def get_token(self, request: HttpRequest) -> str | None: class OrgLevelTokenAuthentication(authentication.TokenAuthentication): - def authenticate_credentials(self, key): + def authenticate_credentials( + self, key: str + ) -> Optional[Tuple[Owner, OrgLevelTokenRepositoryAuth]]: if is_uuid(key): # else, continue to next auth class # Actual verification for org level tokens token = OrganizationLevelToken.objects.filter(token=key).first() @@ -236,7 +249,9 @@ def authenticate_credentials(self, key): class GitHubOIDCTokenAuthentication(authentication.TokenAuthentication): - def authenticate_credentials(self, token): + def authenticate_credentials( + self, token: str + ) -> Optional[Tuple[RepositoryAsUser, OIDCTokenRepositoryAuth]]: if not token or is_uuid(token): return None # continue to next auth class @@ -283,7 +298,12 @@ def _get_info_from_request_path( return repo, commitid - def get_branch(self, request, repoid=None, commitid=None): + def get_branch( + self, + request: HttpRequest, + repoid: Optional[int] = None, + commitid: Optional[str] = None, + ) -> Optional[str]: if repoid and commitid: commit = Commit.objects.filter( repository_id=repoid, commitid=commitid @@ -299,7 +319,9 @@ def get_branch(self, request, repoid=None, commitid=None): else: return body.get("branch") - def authenticate(self, request): + def authenticate( + self, request: HttpRequest + ) -> Tuple[RepositoryAsUser, TokenlessAuth]: repository, commitid = self._get_info_from_request_path(request) if repository is None or repository.private: @@ -341,7 +363,12 @@ def _get_info_from_request_path( # Validate provider raise exceptions.AuthenticationFailed(self.auth_failed_message) - def get_branch(self, request, repoid=None, commitid=None): + def get_branch( + self, + request: HttpRequest, + repoid: Optional[int] = None, + commitid: Optional[str] = None, + ) -> str: body = json.loads(str(request.body, "utf8")) # If commit is not created yet (ie first upload for this commit), we just validate branch format. @@ -419,7 +446,7 @@ class UploadTokenRequiredGetFromBodyAuthenticationCheck( then use the same authenticate() as parent class. """ - def _get_git(self, validated_data): + def _get_git(self, validated_data: Dict[str, str]) -> Optional[str]: """ BA sends this in as git_service, TA sends this in as service. Use this function so this Check class can be used by both views. diff --git a/codecov_auth/commands/owner/interactors/get_uploads_number_per_user.py b/codecov_auth/commands/owner/interactors/get_uploads_number_per_user.py index a0027f9d7e..02fe9d2465 100644 --- a/codecov_auth/commands/owner/interactors/get_uploads_number_per_user.py +++ b/codecov_auth/commands/owner/interactors/get_uploads_number_per_user.py @@ -1,3 +1,5 @@ +from typing import Optional + from shared.plan.service import PlanService from shared.upload.utils import query_monthly_coverage_measurements @@ -11,7 +13,7 @@ class GetUploadsNumberPerUserInteractor(BaseInteractor): @sync_to_async - def execute(self, owner: Owner): + def execute(self, owner: Owner) -> Optional[int]: plan_service = PlanService(current_org=owner) monthly_limit = plan_service.monthly_uploads_limit if monthly_limit is not None: diff --git a/core/commands/repository/repository.py b/core/commands/repository/repository.py index a40c484cd7..49bf521e7f 100644 --- a/core/commands/repository/repository.py +++ b/core/commands/repository/repository.py @@ -1,10 +1,10 @@ import uuid -from typing import Optional +from typing import Awaitable, Optional from codecov.commands.base import BaseCommand -from codecov_auth.models import Owner +from codecov_auth.models import Owner, RepositoryToken from core.models import Repository -from timeseries.models import MeasurementName +from timeseries.models import Dataset, MeasurementName from .interactors.activate_measurements import ActivateMeasurementsInteractor from .interactors.encode_secret_string import EncodeSecretStringInteractor @@ -22,8 +22,8 @@ class RepositoryCommands(BaseCommand): def fetch_repository( self, - owner, - name, + owner: Owner, + name: str, okta_authenticated_accounts: list[int], exclude_okta_enforced_repos: bool = True, ) -> Repository: @@ -38,7 +38,7 @@ def regenerate_repository_upload_token( self, repo_name: str, owner_username: str, - ) -> uuid.UUID: + ) -> Awaitable[uuid.UUID]: return self.get_interactor(RegenerateRepositoryUploadTokenInteractor).execute( repo_name, owner_username ) @@ -49,37 +49,39 @@ def update_repository( owner: Owner, default_branch: Optional[str], activated: Optional[bool], - ): + ) -> None: return self.get_interactor(UpdateRepositoryInteractor).execute( repo_name, owner, default_branch, activated ) - def get_upload_token(self, repository): + def get_upload_token(self, repository: Repository) -> uuid.UUID: return self.get_interactor(GetUploadTokenInteractor).execute(repository) - def get_repository_token(self, repository, token_type): + def get_repository_token( + self, repository: Repository, token_type: RepositoryToken.TokenType + ) -> str: return self.get_interactor(GetRepositoryTokenInteractor).execute( repository, token_type ) def regenerate_repository_token( - self, repo_name: str, owner_username: str, token_type: str - ): + self, repo_name: str, owner_username: str, token_type: RepositoryToken.TokenType + ) -> str: return self.get_interactor(RegenerateRepositoryTokenInteractor).execute( repo_name, owner_username, token_type ) def activate_measurements( self, repo_name: str, owner_name: str, measurement_type: MeasurementName - ): + ) -> Dataset: return self.get_interactor(ActivateMeasurementsInteractor).execute( repo_name, owner_name, measurement_type ) - def erase_repository(self, repo_name: str, owner: Owner): + def erase_repository(self, repo_name: str, owner: Owner) -> None: return self.get_interactor(EraseRepositoryInteractor).execute(repo_name, owner) - def encode_secret_string(self, owner: Owner, repo_name: str, value: str): + def encode_secret_string(self, owner: Owner, repo_name: str, value: str) -> str: return self.get_interactor(EncodeSecretStringInteractor).execute( owner, repo_name, value ) diff --git a/core/signals.py b/core/signals.py index 99f22c4cd1..37afc20d80 100644 --- a/core/signals.py +++ b/core/signals.py @@ -1,5 +1,5 @@ import logging -from typing import Any, Dict, List, Type +from typing import Any, Dict, List, Type, cast from django.db.models.signals import post_save from django.dispatch import receiver @@ -16,7 +16,7 @@ def update_repository( sender: Type[Repository], instance: Repository, **kwargs: Dict[str, Any] ) -> None: log.info(f"Signal triggered for repository {instance.repoid}") - created: bool = kwargs["created"] + created: bool = cast(bool, kwargs["created"]) changes: Dict[str, Any] = instance.tracker.changed() tracked_fields: List[str] = ["name", "upload_token", "activated", "active"] diff --git a/graphql_api/types/account/account.py b/graphql_api/types/account/account.py index 92e0151277..8076961cc9 100644 --- a/graphql_api/types/account/account.py +++ b/graphql_api/types/account/account.py @@ -1,8 +1,10 @@ +from typing import Any, Coroutine, Optional + from ariadne import ObjectType from graphql import GraphQLResolveInfo from codecov.db import sync_to_async -from codecov_auth.models import Account, OktaSettings +from codecov_auth.models import Account, OktaSettings, Owner from graphql_api.helpers.ariadne import ariadne_load_local_graphql from graphql_api.helpers.connection import ( build_connection_graphql, @@ -43,9 +45,9 @@ def resolve_activated_user_count(account: Account, info: GraphQLResolveInfo) -> def resolve_organizations( account: Account, info: GraphQLResolveInfo, - ordering_direction=OrderingDirection.ASC, - **kwargs, -): + ordering_direction: Optional[OrderingDirection] = OrderingDirection.ASC, + **kwargs: Any, +) -> Coroutine[Any, Any, Owner]: return queryset_to_connection( account.organizations, ordering=("username",), diff --git a/graphql_api/types/mutation/erase_repository/erase_repository.py b/graphql_api/types/mutation/erase_repository/erase_repository.py index 2e6bafbae0..321590f39c 100644 --- a/graphql_api/types/mutation/erase_repository/erase_repository.py +++ b/graphql_api/types/mutation/erase_repository/erase_repository.py @@ -1,4 +1,7 @@ +from typing import Any, Dict + from ariadne import UnionType +from graphql import GraphQLResolveInfo from graphql_api.helpers.mutation import ( require_authenticated, @@ -9,7 +12,9 @@ @wrap_error_handling_mutation @require_authenticated -async def resolve_erase_repository(_, info, input) -> None: +async def resolve_erase_repository( + _: Any, info: GraphQLResolveInfo, input: Dict[str, Any] +) -> None: command = info.context["executor"].get_command("repository") current_owner = info.context["request"].current_owner repo_name = input.get("repo_name") diff --git a/graphql_api/types/mutation/regenerate_repository_upload_token/regenerate_repository_upload_token.py b/graphql_api/types/mutation/regenerate_repository_upload_token/regenerate_repository_upload_token.py index 60e51f8922..0d6a6a8e99 100644 --- a/graphql_api/types/mutation/regenerate_repository_upload_token/regenerate_repository_upload_token.py +++ b/graphql_api/types/mutation/regenerate_repository_upload_token/regenerate_repository_upload_token.py @@ -1,4 +1,8 @@ +import uuid +from typing import Any, Dict + from ariadne import UnionType +from graphql import GraphQLResolveInfo from core.commands.repository.repository import RepositoryCommands from graphql_api.helpers.mutation import ( @@ -10,11 +14,13 @@ @wrap_error_handling_mutation @require_authenticated -async def resolve_regenerate_repository_upload_token(_, info, input): +async def resolve_regenerate_repository_upload_token( + _: Any, info: GraphQLResolveInfo, input: Dict[str, str] +) -> Dict[str, uuid.UUID]: command: RepositoryCommands = info.context["executor"].get_command("repository") token = await command.regenerate_repository_upload_token( - repo_name=input.get("repo_name"), - owner_username=input.get("owner"), + repo_name=input.get("repo_name", ""), + owner_username=input.get("owner", ""), ) return {"token": token} diff --git a/graphql_api/types/owner/owner.py b/graphql_api/types/owner/owner.py index b6314463f5..589a3d1d64 100644 --- a/graphql_api/types/owner/owner.py +++ b/graphql_api/types/owner/owner.py @@ -1,6 +1,6 @@ from datetime import datetime from hashlib import sha1 -from typing import Any, Iterable, List, Optional +from typing import Any, Coroutine, Iterable, List, Optional import shared.rate_limits as rate_limits import stripe @@ -62,7 +62,7 @@ def resolve_repositories( ordering: Optional[RepositoryOrdering] = RepositoryOrdering.ID, ordering_direction: Optional[OrderingDirection] = OrderingDirection.ASC, **kwargs: Any, -) -> Connection: +) -> Coroutine[Any, Any, Connection]: current_owner = info.context["request"].current_owner okta_account_auths: list[int] = info.context["request"].session.get( OKTA_SIGNED_IN_ACCOUNTS_SESSION_KEY, [] diff --git a/graphql_api/types/repository/repository.py b/graphql_api/types/repository/repository.py index e53992727f..1dfa95b6a3 100644 --- a/graphql_api/types/repository/repository.py +++ b/graphql_api/types/repository/repository.py @@ -1,6 +1,6 @@ import logging from datetime import datetime -from typing import List, Optional +from typing import Any, Dict, List, Optional import shared.rate_limits as rate_limits import yaml @@ -9,8 +9,8 @@ from graphql.type.definition import GraphQLResolveInfo from codecov.db import sync_to_async -from codecov_auth.models import SERVICE_GITHUB, SERVICE_GITHUB_ENTERPRISE -from core.models import Branch, Repository +from codecov_auth.models import SERVICE_GITHUB, SERVICE_GITHUB_ENTERPRISE, Owner +from core.models import Branch, Commit, Pull, Repository from graphql_api.actions.commits import repo_commits from graphql_api.dataloader.commit import CommitLoader from graphql_api.dataloader.owner import OwnerLoader @@ -21,6 +21,7 @@ CoverageAnalyticsProps, ) from graphql_api.types.enums import OrderingDirection +from graphql_api.types.enums.enum_types import PullRequestState from graphql_api.types.errors.errors import NotFoundError, OwnerNotActivatedError from services.profiling import CriticalFile, ProfilingSummary from services.redis_configuration import get_redis_connection @@ -59,18 +60,18 @@ def resolve_branch( @repository_bindable.field("author") -def resolve_author(repository: Repository, info: GraphQLResolveInfo): +def resolve_author(repository: Repository, info: GraphQLResolveInfo) -> Owner: return OwnerLoader.loader(info).load(repository.author_id) @repository_bindable.field("commit") -def resolve_commit(repository: Repository, info: GraphQLResolveInfo, id): +def resolve_commit(repository: Repository, info: GraphQLResolveInfo, id: int) -> Commit: loader = CommitLoader.loader(info, repository.pk) return loader.load(id) @repository_bindable.field("uploadToken") -def resolve_upload_token(repository: Repository, info: GraphQLResolveInfo): +def resolve_upload_token(repository: Repository, info: GraphQLResolveInfo) -> str: should_hide_tokens = settings.HIDE_ALL_CODECOV_TOKENS current_owner = info.context["request"].current_owner @@ -86,7 +87,7 @@ def resolve_upload_token(repository: Repository, info: GraphQLResolveInfo): @repository_bindable.field("pull") -def resolve_pull(repository: Repository, info: GraphQLResolveInfo, id): +def resolve_pull(repository: Repository, info: GraphQLResolveInfo, id: int) -> Pull: command = info.context["executor"].get_command("pull") return command.fetch_pull_request(repository, id) @@ -95,10 +96,10 @@ def resolve_pull(repository: Repository, info: GraphQLResolveInfo, id): async def resolve_pulls( repository: Repository, info: GraphQLResolveInfo, - filters=None, - ordering_direction=OrderingDirection.DESC, - **kwargs, -): + filters: Optional[Dict[str, List[PullRequestState]]] = None, + ordering_direction: Optional[OrderingDirection] = OrderingDirection.DESC, + **kwargs: Any, +) -> List[Pull]: command = info.context["executor"].get_command("pull") queryset = await command.fetch_pull_requests(repository, filters) return await queryset_to_connection( @@ -111,8 +112,11 @@ async def resolve_pulls( @repository_bindable.field("commits") async def resolve_commits( - repository: Repository, info: GraphQLResolveInfo, filters=None, **kwargs -): + repository: Repository, + info: GraphQLResolveInfo, + filters: Optional[Dict[str, Any]] = None, + **kwargs: Any, +) -> List[Commit]: queryset = await sync_to_async(repo_commits)(repository, filters) connection = await queryset_to_connection( queryset, @@ -132,8 +136,11 @@ async def resolve_commits( @repository_bindable.field("branches") async def resolve_branches( - repository: Repository, info: GraphQLResolveInfo, filters=None, **kwargs -): + repository: Repository, + info: GraphQLResolveInfo, + filters: Optional[Dict[str, str | bool]] = None, + **kwargs: Any, +) -> List[Branch]: command = info.context["executor"].get_command("branch") queryset = await command.fetch_branches(repository, filters) return await queryset_to_connection( @@ -145,18 +152,20 @@ async def resolve_branches( @repository_bindable.field("defaultBranch") -def resolve_default_branch(repository: Repository, info: GraphQLResolveInfo): +def resolve_default_branch(repository: Repository, info: GraphQLResolveInfo) -> str: return repository.branch @repository_bindable.field("profilingToken") -def resolve_profiling_token(repository: Repository, info: GraphQLResolveInfo): +def resolve_profiling_token(repository: Repository, info: GraphQLResolveInfo) -> str: command = info.context["executor"].get_command("repository") return command.get_repository_token(repository, token_type="profiling") @repository_bindable.field("staticAnalysisToken") -def resolve_static_analysis_token(repository: Repository, info: GraphQLResolveInfo): +def resolve_static_analysis_token( + repository: Repository, info: GraphQLResolveInfo +) -> str: command = info.context["executor"].get_command("repository") return command.get_repository_token(repository, token_type="static_analysis") @@ -178,12 +187,14 @@ def resolve_critical_files( @repository_bindable.field("graphToken") -def resolve_graph_token(repository: Repository, info: GraphQLResolveInfo): +def resolve_graph_token(repository: Repository, info: GraphQLResolveInfo) -> str: return repository.image_token @repository_bindable.field("yaml") -def resolve_repo_yaml(repository: Repository, info: GraphQLResolveInfo): +def resolve_repo_yaml( + repository: Repository, info: GraphQLResolveInfo +) -> Optional[str]: if repository.yaml is None: return None return yaml.dump(repository.yaml) @@ -191,7 +202,9 @@ def resolve_repo_yaml(repository: Repository, info: GraphQLResolveInfo): @repository_bindable.field("bot") @sync_to_async -def resolve_repo_bot(repository: Repository, info: GraphQLResolveInfo): +def resolve_repo_bot( + repository: Repository, info: GraphQLResolveInfo +) -> Optional[Owner]: return repository.bot @@ -212,7 +225,9 @@ def resolve_is_ats_configured(repository: Repository, info: GraphQLResolveInfo) @repository_bindable.field("repositoryConfig") -def resolve_repository_config(repository: Repository, info: GraphQLResolveInfo): +def resolve_repository_config( + repository: Repository, info: GraphQLResolveInfo +) -> Repository: return repository @@ -251,7 +266,7 @@ def resolve_coverage_enabled( @repository_result_bindable.type_resolver -def resolve_repository_result_type(obj, *_): +def resolve_repository_result_type(obj: Any, *_: Any) -> Optional[str]: if isinstance(obj, Repository): return "Repository" elif isinstance(obj, OwnerNotActivatedError): @@ -262,7 +277,9 @@ def resolve_repository_result_type(obj, *_): @repository_bindable.field("isFirstPullRequest") @sync_to_async -def resolve_is_first_pull_request(repository: Repository, info) -> bool: +def resolve_is_first_pull_request( + repository: Repository, info: GraphQLResolveInfo +) -> bool: has_one_pr = repository.pull_requests.count() == 1 if has_one_pr: @@ -274,7 +291,9 @@ def resolve_is_first_pull_request(repository: Repository, info) -> bool: @repository_bindable.field("isGithubRateLimited") @sync_to_async -def resolve_is_github_rate_limited(repository: Repository, info) -> bool | None: +def resolve_is_github_rate_limited( + repository: Repository, info: GraphQLResolveInfo +) -> bool | None: if ( repository.service != SERVICE_GITHUB and repository.service != SERVICE_GITHUB_ENTERPRISE diff --git a/services/profiling.py b/services/profiling.py index a6fd90e415..a2e4b361f9 100644 --- a/services/profiling.py +++ b/services/profiling.py @@ -16,7 +16,7 @@ class CriticalFile: - def __init__(self, name): + def __init__(self, name: str) -> None: self.name = name @@ -60,7 +60,7 @@ def summary_data( return None def _get_critical_files_from_yaml( - self, profiling_commit: ProfilingCommit = None + self, profiling_commit: Optional[ProfilingCommit] = None ) -> List[str]: """ Get a list of files present in the commit report that are also marked as critical in the repo yaml (under profiling.critical_files_paths) @@ -79,7 +79,13 @@ def _get_critical_files_from_yaml( "critical_files_paths" ): return [] - commit_sha = self.commit_sha or profiling_commit.commit_sha + + commit_sha = None + if self.commit_sha: + commit_sha = self.commit_sha + elif profiling_commit: + commit_sha = profiling_commit.commit_sha + commit = Commit.objects.get(commitid=commit_sha) report = report_service.build_report_from_commit(commit) if report is None: diff --git a/services/report.py b/services/report.py index d4f9dd9270..19a2647a9d 100644 --- a/services/report.py +++ b/services/report.py @@ -29,13 +29,13 @@ def _sessions_with_specific_flags( def files_in_sessions(commit_report: Report, session_ids: list[int]) -> list[str]: - files, session_ids = [], set(session_ids) + files, session_ids_set = [], set(session_ids) for file in commit_report: found = False for line in file: if line: for session in line.sessions: - if session.id in session_ids: + if session.id in session_ids_set: found = True break if found: diff --git a/services/yaml.py b/services/yaml.py index 80ecea78e8..c9a395769e 100644 --- a/services/yaml.py +++ b/services/yaml.py @@ -1,7 +1,7 @@ import enum import logging from functools import lru_cache -from typing import Dict +from typing import Dict, Optional from asgiref.sync import async_to_sync from shared.yaml import UserYaml, fetch_current_yaml_from_provider_via_reference @@ -70,6 +70,6 @@ def final_commit_yaml(commit: Commit, owner: Owner | None) -> UserYaml: ) -def get_yaml_state(yaml: UserYaml) -> YamlStates: +def get_yaml_state(yaml: UserYaml) -> Optional[YamlStates]: if yaml == get_config("site", default={}): return YamlStates.DEFAULT diff --git a/upload/throttles.py b/upload/throttles.py index 949ca689a8..0b8069a178 100644 --- a/upload/throttles.py +++ b/upload/throttles.py @@ -3,8 +3,10 @@ from django.conf import settings from django.core.exceptions import ObjectDoesNotExist from django.db.models import Q +from django.http import HttpRequest from rest_framework.exceptions import ValidationError from rest_framework.throttling import BaseThrottle +from rest_framework.views import APIView from shared.plan.service import PlanService from shared.reports.enums import UploadType from shared.upload.utils import query_monthly_coverage_measurements @@ -19,7 +21,7 @@ class UploadsPerCommitThrottle(BaseThrottle): - def allow_request(self, request, view): + def allow_request(self, request: HttpRequest, view: APIView) -> bool: try: repository = view.get_repo() commit = view.get_commit(repository) @@ -44,7 +46,7 @@ def allow_request(self, request, view): class UploadsPerWindowThrottle(BaseThrottle): - def allow_request(self, request, view): + def allow_request(self, request: HttpRequest, view: APIView) -> bool: try: repository = view.get_repo() commit = view.get_commit(repository) diff --git a/upload/tokenless/github_actions.py b/upload/tokenless/github_actions.py index 2a14c4d999..b508351544 100644 --- a/upload/tokenless/github_actions.py +++ b/upload/tokenless/github_actions.py @@ -1,5 +1,6 @@ import logging from datetime import datetime, timedelta +from typing import Any, Dict from asgiref.sync import async_to_sync from django.conf import settings @@ -18,7 +19,7 @@ class TokenlessGithubActionsHandler(BaseTokenlessUploadHandler): client_id = settings.GITHUB_CLIENT_ID client_secret = settings.GITHUB_CLIENT_SECRET - def log_warning(self, message): + def log_warning(self, message: str) -> None: log.warning( message, extra=dict( @@ -29,7 +30,7 @@ def log_warning(self, message): ), ) - def get_build(self): + def get_build(self) -> Dict[str, Any]: git = get( "github", token=dict(key=self.actions_token), @@ -73,7 +74,7 @@ def get_build(self): return actions_response - def verify(self): + def verify(self) -> str: if not self.upload_params.get("owner"): raise NotFound( 'Missing "owner" argument. Please upload with the Codecov repository upload token to resolve issue.' diff --git a/upload/tokenless/travis.py b/upload/tokenless/travis.py index 6f3b50cd5a..0054f89a59 100644 --- a/upload/tokenless/travis.py +++ b/upload/tokenless/travis.py @@ -1,5 +1,6 @@ import logging from datetime import datetime, timedelta +from typing import Any, Dict import requests from requests.exceptions import ConnectionError, HTTPError @@ -12,7 +13,7 @@ class TokenlessTravisHandler(BaseTokenlessUploadHandler): - def get_build(self): + def get_build(self) -> Dict[str, Any]: travis_dot_com = False try: @@ -88,7 +89,7 @@ def get_build(self): return build.json() - def verify(self): + def verify(self) -> str: # find repo in travis.com job = self.get_build() diff --git a/upload/views/commits.py b/upload/views/commits.py index 804b42af09..2ed1aa9a5c 100644 --- a/upload/views/commits.py +++ b/upload/views/commits.py @@ -1,7 +1,12 @@ import logging +from typing import Any, Callable, Dict +from django.db.models import QuerySet +from django.http import HttpRequest +from rest_framework import serializers from rest_framework.exceptions import NotAuthenticated from rest_framework.generics import ListCreateAPIView +from rest_framework.response import Response from shared.metrics import inc_counter from codecov_auth.authentication.repo_auth import ( @@ -13,7 +18,7 @@ UploadTokenRequiredAuthenticationCheck, repo_auth_custom_exception_handler, ) -from core.models import Commit +from core.models import Commit, Repository from upload.helpers import ( generate_upload_prometheus_metrics_labels, validate_activated_repo, @@ -26,7 +31,9 @@ log = logging.getLogger(__name__) -def create_commit(serializer, repository): +def create_commit( + serializer: serializers.ModelSerializer, repository: Repository +) -> Commit: validate_activated_repo(repository) commit = serializer.save(repository=repository) return commit @@ -44,14 +51,14 @@ class CommitViews(ListCreateAPIView, GetterMixin): TokenlessAuthentication, ] - def get_exception_handler(self): + def get_exception_handler(self) -> Callable[[Exception, Dict[str, Any]], Response]: return repo_auth_custom_exception_handler - def get_queryset(self): + def get_queryset(self) -> QuerySet: repository = self.get_repo() return Commit.objects.filter(repository=repository) - def list(self, request, *args, **kwargs): + def list(self, request: HttpRequest, *args: Any, **kwargs: Any) -> Response: repository = self.get_repo() if repository.private and isinstance( self.request.auth, TokenlessAuthentication @@ -59,10 +66,10 @@ def list(self, request, *args, **kwargs): raise NotAuthenticated() return super().list(request, *args, **kwargs) - def create(self, request, *args, **kwargs): + def create(self, request: HttpRequest, *args: Any, **kwargs: Any) -> Response: return super().create(request, *args, **kwargs) - def perform_create(self, serializer): + def perform_create(self, serializer: CommitSerializer) -> Commit: inc_counter( API_UPLOAD_COUNTER, labels=generate_upload_prometheus_metrics_labels( diff --git a/upload/views/empty_upload.py b/upload/views/empty_upload.py index dfcd3cc422..77a3e52e7f 100644 --- a/upload/views/empty_upload.py +++ b/upload/views/empty_upload.py @@ -1,14 +1,16 @@ import fnmatch import logging -from typing import List +from typing import Any, Callable, List, Optional import regex from asgiref.sync import async_to_sync +from django.http import HttpRequest from rest_framework import serializers, status from rest_framework.exceptions import NotFound from rest_framework.generics import CreateAPIView from rest_framework.response import Response from shared.metrics import inc_counter +from shared.torngit.base import TorngitBaseAdapter from shared.torngit.exceptions import TorngitClientError, TorngitClientGeneralError from codecov_auth.authentication.repo_auth import ( @@ -20,6 +22,7 @@ repo_auth_custom_exception_handler, ) from codecov_auth.authentication.types import RepositoryAsUser +from core.models import Commit from services.repo_providers import RepoProviderService from services.task import TaskService from services.yaml import final_commit_yaml @@ -81,10 +84,10 @@ class EmptyUploadView(CreateAPIView, GetterMixin): RepositoryLegacyTokenAuthentication, ] - def get_exception_handler(self): + def get_exception_handler(self) -> Callable[[Exception, dict[str, Any]], Response]: return repo_auth_custom_exception_handler - def post(self, request, *args, **kwargs): + def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> Response: inc_counter( API_UPLOAD_COUNTER, labels=generate_upload_prometheus_metrics_labels( @@ -188,7 +191,9 @@ def post(self, request, *args, **kwargs): status=status.HTTP_200_OK, ) - def get_changed_files_from_provider(self, commit, provider, pull_id): + def get_changed_files_from_provider( + self, commit: Commit, provider: TorngitBaseAdapter, pull_id: int + ) -> List[str]: try: changed_files = async_to_sync(provider.get_pull_request_files)(pull_id) except TorngitClientError: @@ -203,7 +208,9 @@ def get_changed_files_from_provider(self, commit, provider, pull_id): raise NotFound("Unable to get pull request's files.") return changed_files - def get_pull_request_id(self, commit, provider, pull_id): + def get_pull_request_id( + self, commit: Commit, provider: TorngitBaseAdapter, pull_id: Optional[int] + ) -> int: try: if pull_id is None: pull_id = async_to_sync(provider.find_pull_request)( diff --git a/upload/views/reports.py b/upload/views/reports.py index 3685990f65..a6140d5a91 100644 --- a/upload/views/reports.py +++ b/upload/views/reports.py @@ -1,8 +1,10 @@ import logging +from typing import Any, Callable from django.http import HttpRequest, HttpResponseNotAllowed from rest_framework.exceptions import ValidationError from rest_framework.generics import CreateAPIView, ListCreateAPIView, RetrieveAPIView +from rest_framework.response import Response from shared.metrics import inc_counter from codecov_auth.authentication.repo_auth import ( @@ -14,6 +16,7 @@ UploadTokenRequiredAuthenticationCheck, repo_auth_custom_exception_handler, ) +from core.models import Commit, Repository from reports.models import CommitReport, ReportResults from services.task import TaskService from upload.helpers import ( @@ -28,7 +31,9 @@ log = logging.getLogger(__name__) -def create_report(serializer, repository, commit): +def create_report( + serializer: CommitReportSerializer, repository: Repository, commit: Commit +) -> CommitReport: code = serializer.validated_data.get("code") if code == "default": serializer.validated_data["code"] = None @@ -55,10 +60,10 @@ class ReportViews(ListCreateAPIView, GetterMixin): TokenlessAuthentication, ] - def get_exception_handler(self): + def get_exception_handler(self) -> Callable[[Exception, dict[str, Any]], Response]: return repo_auth_custom_exception_handler - def perform_create(self, serializer): + def perform_create(self, serializer: CommitReportSerializer) -> CommitReport: inc_counter( API_UPLOAD_COUNTER, labels=generate_upload_prometheus_metrics_labels( @@ -91,7 +96,9 @@ def perform_create(self, serializer): ) return instance - def list(self, request: HttpRequest, service: str, repo: str, commit_sha: str): + def list( + self, request: HttpRequest, service: str, repo: str, commit_sha: str + ) -> HttpResponseNotAllowed: return HttpResponseNotAllowed(permitted_methods=["POST"]) @@ -111,10 +118,10 @@ class ReportResultsView( TokenlessAuthentication, ] - def get_exception_handler(self): + def get_exception_handler(self) -> Callable[[Exception, dict[str, Any]], Response]: return repo_auth_custom_exception_handler - def perform_create(self, serializer): + def perform_create(self, serializer: ReportResultsSerializer) -> ReportResults: inc_counter( API_UPLOAD_COUNTER, labels=generate_upload_prometheus_metrics_labels( @@ -154,7 +161,7 @@ def perform_create(self, serializer): ) return instance - def get_object(self): + def get_object(self) -> ReportResults: repository = self.get_repo() commit = self.get_commit(repository) report = self.get_report(commit) diff --git a/upload/views/upload_completion.py b/upload/views/upload_completion.py index edb1df6aed..f9b82ace1c 100644 --- a/upload/views/upload_completion.py +++ b/upload/views/upload_completion.py @@ -1,5 +1,7 @@ import logging +from typing import Any, Callable, Dict +from django.http import HttpRequest from rest_framework import status from rest_framework.generics import CreateAPIView from rest_framework.response import Response @@ -33,10 +35,10 @@ class UploadCompletionView(CreateAPIView, GetterMixin): RepositoryLegacyTokenAuthentication, ] - def get_exception_handler(self): + def get_exception_handler(self) -> Callable[[Exception, Dict[str, Any]], Response]: return repo_auth_custom_exception_handler - def post(self, request, *args, **kwargs): + def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> Response: inc_counter( API_UPLOAD_COUNTER, labels=generate_upload_prometheus_metrics_labels(