Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improvements in the GitHub integration BE #3962

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f99db4a
feat: Uninstall GitHub app installation from Flagsmith
novakzaballa May 15, 2024
dc84f96
Update delete_github_configuration test
novakzaballa May 15, 2024
c82235e
Update delete_github_configuration test
novakzaballa May 15, 2024
f84758f
fix: unnecessary test code
novakzaballa May 15, 2024
a8fd0c0
fix: Improve code as per PR review
novakzaballa May 16, 2024
2c43980
fix: Move calls to GH API to github client module
novakzaballa May 16, 2024
409d874
test: code coverage
novakzaballa May 17, 2024
dc48f8b
test: Fix code coverage
novakzaballa May 17, 2024
6fc2f47
fix: Fix custom timeout on API calls. Remove unnecessary query.
novakzaballa May 17, 2024
8395e5e
fix: Apply error handling decorator to class view
novakzaballa May 17, 2024
afb6919
Add Github comment when a segment override was deleted
novakzaballa May 17, 2024
cd6b2bb
Get issue name, and implement search and pagination for the GitHub re…
novakzaballa May 19, 2024
ce21aff
Solve feature external resources tests
novakzaballa May 20, 2024
3a8ffb6
Solve github inegratrions tests
novakzaballa May 20, 2024
4834966
Return state, and update unit tests
novakzaballa May 20, 2024
312357c
Return only the required data in the repository request
novakzaballa May 20, 2024
fcef843
test: Add missing coverage
novakzaballa May 20, 2024
3430883
test: Add missing coverage
novakzaballa May 20, 2024
45c3716
Rename get_github_issue_pr_title_and_state and return metadata
novakzaballa May 20, 2024
f000993
fix: Making the integration compatible with versioning
novakzaballa May 21, 2024
6286188
Merge branch 'main' into feat/uninstall-github-app-installation-from-…
novakzaballa May 21, 2024
3468d8f
test: Add code coverage for validation exception
novakzaballa May 21, 2024
f833596
test: Solve flaky test
novakzaballa May 22, 2024
0344704
feat: support more query params for searching issues and prs
novakzaballa May 22, 2024
1184a93
test: Add coverage for new parameters allowed for querying issues and…
novakzaballa May 22, 2024
e8f89a1
Improve GitHub Integrations code
novakzaballa May 22, 2024
a94630c
Add logger
novakzaballa May 22, 2024
accfc6d
Return Response
novakzaballa May 22, 2024
6f5ea7a
Update types and delete comments
novakzaballa May 22, 2024
f9eb870
Merge branch 'main' into feat/uninstall-github-app-installation-from-…
novakzaballa May 22, 2024
3884545
Solve logger issue
novakzaballa May 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion api/integrations/github/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from github import Auth, Github


def generate_token(installation_id: str, app_id: int) -> str:
def generate_token(installation_id: str, app_id: int) -> str: # pragma: no cover
auth: Auth.AppInstallationAuth = Auth.AppAuth(
app_id=int(app_id), private_key=settings.GITHUB_PEM
).get_installation_auth(
Expand All @@ -12,3 +12,12 @@ def generate_token(installation_id: str, app_id: int) -> str:
Github(auth=auth)
token = auth.token
return token


def generate_jwt_token(app_id: int) -> str: # pragma: no cover
github_auth: Auth.AppAuth = Auth.AppAuth(
app_id=int(app_id),
private_key=settings.GITHUB_PEM,
)
token = github_auth.create_jwt()
return token
19 changes: 18 additions & 1 deletion api/integrations/github/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response

from integrations.github.client import generate_token
from integrations.github.client import generate_jwt_token, generate_token
from integrations.github.constants import GITHUB_API_URL, GITHUB_API_VERSION
from integrations.github.exceptions import DuplicateGitHubIntegration
from integrations.github.helpers import github_webhook_payload_is_valid
Expand Down Expand Up @@ -71,6 +71,23 @@ def create(self, request, *args, **kwargs):
if re.search(r"Key \(organisation_id\)=\(\d+\) already exists", str(e)):
raise DuplicateGitHubIntegration

def destroy(self, request, *args, **kwargs):
instance = self.get_object()
installation_id = instance.installation_id
token = generate_jwt_token(settings.GITHUB_APP_ID)

url = f"{GITHUB_API_URL}app/installations/{installation_id}"

headers = {
"X-GitHub-Api-Version": GITHUB_API_VERSION,
"Accept": "application/vnd.github.v3+json",
"Authorization": f"Bearer {token}",
}

response = requests.delete(url, headers=headers, timeout=10)
response.raise_for_status()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're assuming we're returning 500 in case of any errors from Github here?

Do we want to return the error message to the client or at least log it in some way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, good catch. Done.

return super().destroy(request, *args, **kwargs)


class GithubRepositoryViewSet(viewsets.ModelViewSet):
permission_classes = (
Expand Down
21 changes: 21 additions & 0 deletions api/tests/unit/integrations/github/test_unit_github_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
WEBHOOK_SECRET = "secret-key"


def mocked_requests_delete(*args, **kwargs):
class MockResponse:
def __init__(self, json_data, status_code):
self.json_data = json_data
self.status_code = status_code

def raise_for_status(self) -> None:
pass

return MockResponse(json_data={"data": "data"}, status_code=204)


def test_get_github_configuration(
admin_client_new: APIClient,
organisation: Organisation,
Expand Down Expand Up @@ -105,6 +117,7 @@ def test_delete_github_configuration(
organisation: Organisation,
github_configuration: GithubConfiguration,
github_repository: GithubRepository,
mocker: MockerFixture,
) -> None:
# Given
url = reverse(
Expand All @@ -114,10 +127,18 @@ def test_delete_github_configuration(
github_configuration.id,
],
)

mock_generate_token = mocker.patch(
"integrations.github.views.generate_jwt_token",
)
mock_generate_token.return_value = "mocked_token"
mocker.patch("requests.delete", side_effect=mocked_requests_delete)

# When
response = admin_client_new.delete(url)
# Then
assert response.status_code == status.HTTP_204_NO_CONTENT
assert not GithubConfiguration.objects.filter(id=github_configuration.id).exists()


def test_get_github_repository(
Expand Down