Skip to content

Commit

Permalink
backend: interactor to config repo via PR
Browse files Browse the repository at this point in the history
THese changes extend the graphQL schema to introduce an interactor
that _would_ call a task in the worker to config a repo via PR.

context: codecov/engineering-team#150
  • Loading branch information
giovanni-guidini committed Oct 23, 2023
1 parent 50de754 commit 4e09507
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 0 deletions.
35 changes: 35 additions & 0 deletions core/commands/repository/interactors/config_repo_via_PR.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from codecov.commands.base import BaseInteractor
from codecov.commands.exceptions import ValidationError
from codecov.db import sync_to_async
from codecov_auth.models import Owner
from core.models import Repository
from graphql_api.types.enums.enums import CiProvider


class ConfigureRepoViaPRInteractor(BaseInteractor):

supported_ci_providers = [CiProvider.GITHUB_ACTIONS]

@sync_to_async
def execute(self, repo_name: str, owner_username: str, ci_provider: CiProvider):
author = Owner.objects.filter(
username=owner_username, service=self.service
).first()
repo = (
Repository.objects.viewable_repos(self.current_owner)
.filter(author=author, name=repo_name)
.first()
)
if not repo:
raise ValidationError("Repo not found")

if ci_provider not in self.supported_ci_providers:
raise ValidationError(f"Provider {ci_provider} is not supported")

try:
# TODO: Enqueue task to generate the config PR
raise NotImplementedError()
except NotImplementedError:
result = False

return result
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import pytest
from asgiref.sync import async_to_sync
from django.contrib.auth.models import AnonymousUser
from django.test import TransactionTestCase

from codecov.commands.exceptions import Unauthenticated, ValidationError
from codecov_auth.tests.factories import OwnerFactory
from core.commands.repository.interactors.config_repo_via_PR import (
ConfigureRepoViaPRInteractor,
)
from core.tests.factories import RepositoryFactory, RepositoryTokenFactory
from graphql_api.types.enums.enums import CiProvider

from ..regenerate_repository_token import RegenerateRepositoryTokenInteractor


class TestConfigRepoViaPRInteractor(TransactionTestCase):
def setUp(self):
self.org = OwnerFactory(username="codecov")
self.new_repo = RepositoryFactory(
author=self.org, name="new-repo", active=False
)
self.user = OwnerFactory(
organizations=[self.org.ownerid],
permission=[self.new_repo.repoid],
)
self.random_user = OwnerFactory(organizations=[self.org.ownerid])

def execute(self, owner, repo, provider):
return ConfigureRepoViaPRInteractor(owner, "github").execute(
repo_name=repo.name,
owner_username=self.org.username,
ci_provider=provider,
)

async def test_when_validation_error_ci_provider_not_supported(self):
missing_provider = "CIRCLECI"
with pytest.raises(ValidationError) as exp:
await self.execute(
owner=self.user, repo=self.new_repo, provider=missing_provider
)
assert str(exp.value) == f"Provider {missing_provider} is not supported"

async def test_when_validation_error_repo_not_found(self):
with pytest.raises(ValidationError):
await ConfigureRepoViaPRInteractor(self.random_user, "github").execute(
repo_name="missing-repo",
owner_username=self.org.username,
ci_provider=CiProvider.GITHUB_ACTIONS,
)

async def test_validation_ok(self):
result = await self.execute(
owner=self.user, repo=self.new_repo, provider=CiProvider.GITHUB_ACTIONS
)
assert result is False
9 changes: 9 additions & 0 deletions core/commands/repository/repository.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from codecov.commands.base import BaseCommand
from graphql_api.types.enums.enums import CiProvider
from timeseries.models import MeasurementName

from .interactors.activate_measurements import ActivateMeasurementsInteractor
from .interactors.config_repo_via_PR import ConfigureRepoViaPRInteractor
from .interactors.fetch_repository import FetchRepositoryInteractor
from .interactors.get_repository_token import GetRepositoryTokenInteractor
from .interactors.get_upload_token import GetUploadTokenInteractor
Expand Down Expand Up @@ -33,3 +35,10 @@ def activate_measurements(
return self.get_interactor(ActivateMeasurementsInteractor).execute(
repo_name, owner_name, measurement_type
)

def configure_repo_via_PR(
self, repo_name: str, owner_name: str, ci_provider: CiProvider
):
return self.get_interactor(ConfigureRepoViaPRInteractor).execute(
repo_name, owner_name, ci_provider
)
1 change: 1 addition & 0 deletions graphql_api/types/enums/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .enums import (
CiProvider,
CommitErrorCode,
CommitErrorGeneralType,
CoverageLine,
Expand Down
3 changes: 3 additions & 0 deletions graphql_api/types/enums/ci_provider.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
enum CiProvider {
GITHUB_ACTIONS
}
4 changes: 4 additions & 0 deletions graphql_api/types/enums/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ class LoginProvider(enum.Enum):
OKTA = "okta"


class CiProvider(enum.Enum):
GITHUB_ACTIONS = "github_actions"


class CommitErrorGeneralType(enum.Enum):
yaml_error = "YAML_ERROR"
bot_error = "BOT_ERROR"
Expand Down
5 changes: 5 additions & 0 deletions graphql_api/types/inputs/config_via_PR.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
input ConfigRepositoryViaPRInput {
owner: String!
repoName: String!
ciProvider: CiProvider!
}
2 changes: 2 additions & 0 deletions graphql_api/types/mutation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from .activate_measurements import gql_activate_measurements
from .cancel_trial import gql_cancel_trial
from .config_repo_via_PR import gql_config_repo_via_pr
from .create_api_token import gql_create_api_token
from .create_user_token import gql_create_user_token
from .delete_flag import gql_delete_flag
Expand Down Expand Up @@ -37,3 +38,4 @@
mutation = mutation + gql_save_terms_agreement
mutation = mutation + gql_start_trial
mutation = mutation + gql_cancel_trial
mutation = mutation + gql_config_repo_via_pr
10 changes: 10 additions & 0 deletions graphql_api/types/mutation/config_repo_via_PR/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from graphql_api.helpers.ariadne import ariadne_load_local_graphql

from .config_repo_via_PR import (
error_configure_repo_via_PR,
resolve_config_repository_via_PR,
)

gql_config_repo_via_pr = ariadne_load_local_graphql(
__file__, "config_repo_via_PR.graphql"
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
union ConfigRepositoryViaPRError = UnauthenticatedError | ValidationError

type ConfigRepositoryViaPRPayload {
success: Int
error: ConfigRepositoryViaPRError
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from ariadne import UnionType, convert_kwargs_to_snake_case

from graphql_api.helpers.mutation import (
require_authenticated,
resolve_union_error_type,
wrap_error_handling_mutation,
)


@wrap_error_handling_mutation
@require_authenticated
@convert_kwargs_to_snake_case
async def resolve_config_repository_via_PR(_, info, input):
command = info.context["executor"].get_command("repository")
# FIXME
await command.regenerate_repository_token(
repo_name=input.get("repo_name"),
owner_username=input.get("owner"),
token_type=input.get("token_type"),
)

return {"success": int(True)}


error_configure_repo_via_PR = UnionType("ConfigRepositoryViaPRError")
error_configure_repo_via_PR.type_resolver(resolve_union_error_type)
3 changes: 3 additions & 0 deletions graphql_api/types/mutation/mutation.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ type Mutation {
regenerateRepositoryToken(
input: RegenerateRepositoryTokenInput!
): RegenerateRepositoryTokenPayload
configRepositoryViaPR(
input: ConfigRepositoryViaPRInput!
): ConfigRepositoryViaPRPayload
activateMeasurements(
input: ActivateMeasurementsInput!
): activateMeasurementsPayload
Expand Down

0 comments on commit 4e09507

Please sign in to comment.