Skip to content

Commit

Permalink
feat: Add functions to get/set apps to commits (#469)
Browse files Browse the repository at this point in the history
  • Loading branch information
giovanni-guidini committed May 28, 2024
1 parent d6168c2 commit 1b7be5f
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 0 deletions.
48 changes: 48 additions & 0 deletions services/github.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import logging
from typing import Optional

from redis import RedisError
from shared.github import InvalidInstallationError
from shared.github import get_github_integration_token as _get_github_integration_token

from database.models.core import Commit
from helpers.cache import cache
from helpers.exceptions import RepositoryWithoutValidBotError
from services.redis import get_redis_connection

log = logging.getLogger(__name__)

Expand All @@ -24,3 +27,48 @@ def get_github_integration_token(
except InvalidInstallationError:
log.warning("Failed to get installation token")
raise RepositoryWithoutValidBotError()


COMMIT_GHAPP_KEY_NAME = lambda commit_id: f"app_to_use_for_commit_{commit_id}"
GHAPP_KEY_EXPIRY_SECONDS = 60 * 60 * 2 # 2h


def set_github_app_for_commit(
installation_id: str | int | None, commit: Commit
) -> bool:
"""Sets a GithubAppInstallation.id in Redis as the installation to use for a commit.
Keys live in redis for GHAPP_KEY_EXPIRY_SECONDS before being expired.
Args:
installation_id (str | int | None) - the ID to save.
None -- there was actually no installation ID. Do nothing.
int -- value comes from the Database
str -- value comes from Redis (i.e. the installation was already cached)
commit (Commit) - the commit to attach installation_id to
"""
if installation_id is None:
return False
redis = get_redis_connection()
try:
redis.set(
COMMIT_GHAPP_KEY_NAME(commit.id),
str(installation_id),
ex=GHAPP_KEY_EXPIRY_SECONDS,
)
return True
except RedisError:
log.exception(
"Failed to set app for commit", extra=dict(commit=commit.commitid)
)
return False


def get_github_app_for_commit(commit: Commit) -> str | None:
redis = get_redis_connection()
try:
return redis.get(COMMIT_GHAPP_KEY_NAME(commit.id))
except RedisError:
log.exception(
"Failed to get app for commit", extra=dict(commit=commit.commitid)
)
return None
71 changes: 71 additions & 0 deletions services/tests/test_github.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from unittest.mock import MagicMock

import pytest
from redis import RedisError

from database.models.core import GithubAppInstallation, Owner
from database.tests.factories.core import CommitFactory
from services.github import get_github_app_for_commit, set_github_app_for_commit


class TestGetSetGithubAppsToCommits(object):
def _get_commit(self, dbsession):
commit = CommitFactory(repository__owner__service="github")
dbsession.add(commit)
dbsession.flush()
return commit

def _get_app(self, owner: Owner, dbsession):
app = GithubAppInstallation(
owner=owner, installation_id=1250, app_id=250, pem_path="some_path"
)
dbsession.add(app)
dbsession.flush()
return app

@pytest.fixture
def mock_redis(self, mocker):
fake_redis = MagicMock(name="fake_redis")
mock_conn = mocker.patch("services.github.get_redis_connection")
mock_conn.return_value = fake_redis
return fake_redis

def test_set_app_for_commit_no_app(self, mock_redis, dbsession):
commit = self._get_commit(dbsession)
assert set_github_app_for_commit(None, commit) == False
mock_redis.set.assert_not_called()

def test_set_app_for_commit_redis_success(self, mock_redis, dbsession):
commit = self._get_commit(dbsession)
app = self._get_app(commit.repository.owner, dbsession)
assert set_github_app_for_commit(app.id, commit) == True
mock_redis.set.assert_called_with(
f"app_to_use_for_commit_{commit.id}", str(app.id), ex=(60 * 60 * 2)
)

def test_set_app_for_commit_redis_error(self, mock_redis, dbsession):
commit = self._get_commit(dbsession)
mock_redis.set.side_effect = RedisError
assert set_github_app_for_commit("1000", commit) == False
mock_redis.set.assert_called_with(
f"app_to_use_for_commit_{commit.id}", "1000", ex=(60 * 60 * 2)
)

def test_get_app_for_commit(self, mock_redis):
redis_keys = {
"app_to_use_for_commit_12": "1200",
"app_to_use_for_commit_10": "1000",
}
fake_commit_12 = MagicMock(name="fake_commit", **{"id": 12})
fake_commit_10 = MagicMock(name="fake_commit", **{"id": 10})
fake_commit_50 = MagicMock(name="fake_commit", **{"id": 50})
mock_redis.get.side_effect = lambda key: redis_keys.get(key)
assert get_github_app_for_commit(fake_commit_12) == "1200"
assert get_github_app_for_commit(fake_commit_10) == "1000"
assert get_github_app_for_commit(fake_commit_50) == None

def test_get_app_for_commit_error(self, mock_redis):
mock_redis.get.side_effect = RedisError
fake_commit_12 = MagicMock(name="fake_commit", **{"id": 12})
assert get_github_app_for_commit(fake_commit_12) == None
mock_redis.get.assert_called_with("app_to_use_for_commit_12")

0 comments on commit 1b7be5f

Please sign in to comment.