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

fix: Move call to GitHub integration tasks out from trigger_feature_state_change_webhooks #3905

46 changes: 46 additions & 0 deletions api/features/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import typing
import uuid
from copy import deepcopy
from dataclasses import asdict

from core.models import (
AbstractBaseExportableModel,
Expand All @@ -22,6 +23,7 @@
from django.utils.translation import ugettext_lazy as _
from django_lifecycle import (
AFTER_CREATE,
AFTER_SAVE,
BEFORE_CREATE,
BEFORE_SAVE,
LifecycleModelMixin,
Expand Down Expand Up @@ -72,6 +74,7 @@
STRING,
)
from features.versioning.models import EnvironmentFeatureVersion
from integrations.github.models import GithubConfiguration
from projects.models import Project
from projects.tags.models import Tag

Expand Down Expand Up @@ -989,6 +992,49 @@ def copy_identity_feature_states(
# Save changes to target feature_state
target_feature_state.save()

@hook(AFTER_SAVE)
def create_github_comment(self) -> None:
from integrations.github.github import GithubData, generate_data
from integrations.github.tasks import (
call_github_app_webhook_for_feature_state,
)
from webhooks.webhooks import WebhookEventType

if (
not self.identity_id
and not self.feature_segment
and self.feature.external_resources.exists()
and self.environment.project.github_project.exists()
and self.environment.project.organisation.github_config.exists()
):
github_configuration = GithubConfiguration.objects.get(
organisation_id=self.environment.project.organisation_id
)
feature_states = []
feature_states.append(self)

if self.deleted_at is None:
feature_data: GithubData = generate_data(
github_configuration=github_configuration,
feature_id=self.feature.id,
feature_name=self.feature.name,
type=WebhookEventType.FLAG_UPDATED.value,
feature_states=feature_states,
)

if self.deleted_at is not None:
feature_data: GithubData = generate_data(
github_configuration=github_configuration,
feature_id=self.feature.id,
feature_name=self.feature.name,
type=WebhookEventType.FLAG_DELETED.value,
feature_states=feature_states,
)

call_github_app_webhook_for_feature_state.delay(
args=(asdict(feature_data),),
)


class FeatureStateValue(
AbstractBaseFeatureValueModel,
Expand Down
37 changes: 0 additions & 37 deletions api/features/tasks.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import logging
from dataclasses import asdict

from environments.models import Webhook
from features.models import Feature, FeatureState
from integrations.github.github import GithubData, generate_data
from integrations.github.tasks import call_github_app_webhook_for_feature_state
from organisations.models import Organisation
from task_processor.decorators import register_task_handler
from webhooks.constants import WEBHOOK_DATETIME_FORMAT
from webhooks.webhooks import (
Expand Down Expand Up @@ -59,39 +55,6 @@ def trigger_feature_state_change_webhooks(
)
)

if (
not instance.identity_id
and not instance.feature_segment
and instance.feature.external_resources.exists()
and instance.environment.project.github_project.exists()
and hasattr(instance.environment.project.organisation, "github_config")
):
github_configuration = (
Organisation.objects.prefetch_related("github_config")
.get(id=instance.environment.project.organisationn_id)
.github_config.first()
)
feature_state = {
"environment_name": new_state["environment"]["name"],
"feature_value": new_state["enabled"],
}
feature_states = []
feature_states.append(instance)

feature_data: GithubData = generate_data(
github_configuration=github_configuration,
feature_id=history_instance.feature.id,
feature_name=history_instance.feature.name,
type=WebhookEventType.FLAG_UPDATED.value,
feature_states=feature_states,
)

feature_data.feature_states.append(feature_state)

call_github_app_webhook_for_feature_state.delay(
args=(asdict(feature_data),),
)


def _get_previous_state(
instance: FeatureState,
Expand Down
7 changes: 4 additions & 3 deletions api/integrations/github/constants.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
GITHUB_API_URL = "https://api.github.com/"
GITHUB_API_VERSION = "2022-11-28"

LINK_FEATURE_TEXT = "### This pull request is linked to a Flagsmith Feature (%s):\n"
UNLINKED_FEATURE_TEXT = "### The feature flag %s was unlinked from the issue/PR"
UPDATED_FEATURE_TEXT = "### The Flagsmith Feature %s was updated in the environment "
LINK_FEATURE_TEXT = "### This pull request is linked to a Flagsmith Feature (`%s`):\n"
UNLINKED_FEATURE_TEXT = "### The feature flag `%s` was unlinked from the issue/PR"
UPDATED_FEATURE_TEXT = "### The Flagsmith Feature `%s` was updated in the environment "
LAST_UPDATED_FEATURE_TEXT = "Last Updated %s"
DELETED_FEATURE_TEXT = "### The Feature Flag `%s` was deleted"
6 changes: 5 additions & 1 deletion api/integrations/github/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from features.models import FeatureState, FeatureStateValue
from integrations.github.client import generate_token
from integrations.github.constants import (
DELETED_FEATURE_TEXT,
GITHUB_API_URL,
LAST_UPDATED_FEATURE_TEXT,
LINK_FEATURE_TEXT,
Expand Down Expand Up @@ -55,7 +56,7 @@ def post_comment_to_github(
url, json=payload, headers=headers, timeout=10
)

return response.json() if response.status_code == 200 else None
return response.json() if response.status_code == 201 else None
except requests.RequestException as e:
logger.error(f" {e}")
return None
Expand All @@ -71,6 +72,9 @@ def generate_body_comment(
is_removed = event_type == WebhookEventType.FEATURE_EXTERNAL_RESOURCE_REMOVED.value
delete_text = UNLINKED_FEATURE_TEXT % (name,)

if event_type == WebhookEventType.FLAG_DELETED.value:
return DELETED_FEATURE_TEXT % (name,)

if is_removed:
return delete_text

Expand Down
45 changes: 36 additions & 9 deletions api/integrations/github/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ def send_post_request(data: CallGithubData) -> None:
installation_id = data.github_data.installation_id
body = generate_body_comment(feature_name, event_type, feature_states)

if event_type == WebhookEventType.FLAG_UPDATED.value:
if (
event_type == WebhookEventType.FLAG_UPDATED.value
or event_type == WebhookEventType.FLAG_DELETED.value
):
for resource in data.feature_external_resources:
url = resource.get("url")
pathname = urlparse(url).path
Expand Down Expand Up @@ -61,8 +64,37 @@ def send_post_request(data: CallGithubData) -> None:
@register_task_handler()
def call_github_app_webhook_for_feature_state(event_data: dict[str, Any]) -> None:

from features.feature_external_resources.models import (
FeatureExternalResource,
)

github_event_data = GithubData.from_dict(event_data)

def generate_feature_external_resources(
feature_external_resources: FeatureExternalResource,
) -> list[dict[str, Any]]:
return [
{
"type": resource.type,
"url": resource.url,
}
for resource in feature_external_resources
]

if github_event_data.type == WebhookEventType.FLAG_DELETED.value:
feature_external_resources = generate_feature_external_resources(
FeatureExternalResource.objects.filter(
feature_id=github_event_data.feature_id
)
)
data = CallGithubData(
event_type=github_event_data.type,
github_data=github_event_data,
feature_external_resources=feature_external_resources,
)
send_post_request(data)
return

if (
github_event_data.type
== WebhookEventType.FEATURE_EXTERNAL_RESOURCE_REMOVED.value
Expand All @@ -76,14 +108,9 @@ def call_github_app_webhook_for_feature_state(event_data: dict[str, Any]) -> Non
return

feature = Feature.objects.get(id=github_event_data.feature_id)
feature_external_resources = feature.external_resources.all()
feature_external_resources = [
{
"type": resource.type,
"url": resource.url,
}
for resource in feature_external_resources
]
feature_external_resources = generate_feature_external_resources(
feature.external_resources.all()
)
data = CallGithubData(
event_type=github_event_data.type,
github_data=github_event_data,
Expand Down
Loading