-
Notifications
You must be signed in to change notification settings - Fork 406
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Make Grafana integration project-level - Add `get_audited_instance_from_audit_log_record` service - Add tags, including feature tags - Improve docs
- Loading branch information
Showing
19 changed files
with
645 additions
and
289 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from core.models import AbstractBaseAuditableModel | ||
|
||
from audit.models import AuditLog | ||
from audit.related_object_type import RelatedObjectType | ||
from features.models import Feature, FeatureState | ||
from features.versioning.models import EnvironmentFeatureVersion | ||
|
||
|
||
def get_audited_instance_from_audit_log_record( | ||
audit_log_record: AuditLog, | ||
) -> AbstractBaseAuditableModel | None: | ||
""" | ||
Given an `AuditLog` model instance, return a model instance that produced the log. | ||
""" | ||
# There's currently four (sigh) ways an audit log record is created: | ||
# 1. Historical record | ||
# 2. Segment priorities changed | ||
# 3. Change request | ||
# 4. Environment feature version published | ||
|
||
# Try getting the historical record first. | ||
if history_record := audit_log_record.history_record: | ||
return history_record.instance | ||
|
||
# Try to infer the model class from `AuditLog.related_object_type`. | ||
match audit_log_record.related_object_type: | ||
# Assume segment priorities change. | ||
case RelatedObjectType.FEATURE.name: | ||
return ( | ||
Feature.objects.all_with_deleted() | ||
.filter( | ||
pk=audit_log_record.related_object_id, | ||
project=audit_log_record.project, | ||
) | ||
.first() | ||
) | ||
|
||
# Assume change request. | ||
case RelatedObjectType.FEATURE_STATE.name: | ||
return ( | ||
FeatureState.objects.all_with_deleted() | ||
.filter( | ||
pk=audit_log_record.related_object_id, | ||
environment=audit_log_record.environment, | ||
) | ||
.first() | ||
) | ||
|
||
# Assume environment feature version. | ||
case RelatedObjectType.EF_VERSION.name: | ||
return ( | ||
EnvironmentFeatureVersion.objects.all_with_deleted() | ||
.filter( | ||
uuid=audit_log_record.related_object_uuid, | ||
environment=audit_log_record.environment, | ||
) | ||
.first() | ||
) | ||
|
||
# All known audit log sources exhausted by now. | ||
# Since `RelatedObjectType` is not a 1:1 mapping to a model class, | ||
# generalised heuristics might be dangerous. | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
from audit.models import AuditLog | ||
from audit.services import get_audited_instance_from_audit_log_record | ||
from features.models import ( | ||
Feature, | ||
FeatureSegment, | ||
FeatureState, | ||
FeatureStateValue, | ||
) | ||
from integrations.grafana.types import GrafanaAnnotation | ||
from segments.models import Segment | ||
|
||
|
||
def _get_feature_tags( | ||
feature: Feature, | ||
) -> list[str]: | ||
return list(feature.tags.values_list("label", flat=True)) | ||
|
||
|
||
def _get_instance_tags_from_audit_log_record( | ||
audit_log_record: AuditLog, | ||
) -> list[str]: | ||
if instance := get_audited_instance_from_audit_log_record(audit_log_record): | ||
if isinstance(instance, Feature): | ||
return [ | ||
f"feature:{instance.name}", | ||
*_get_feature_tags(instance), | ||
] | ||
|
||
if isinstance(instance, FeatureState): | ||
return [ | ||
f"feature:{(feature := instance.feature).name}", | ||
f'flag:{"enabled" if instance.enabled else "disabled"}', | ||
*_get_feature_tags(feature), | ||
] | ||
|
||
if isinstance(instance, FeatureStateValue): | ||
return [ | ||
f"feature:{(feature := instance.feature_state.feature).name}", | ||
*_get_feature_tags(feature), | ||
] | ||
|
||
if isinstance(instance, Segment): | ||
return [f"segment:{instance.name}"] | ||
|
||
if isinstance(instance, FeatureSegment): | ||
return [ | ||
f"feature:{(feature := instance.feature).name}", | ||
f"segment:{instance.segment.name}", | ||
*_get_feature_tags(feature), | ||
] | ||
|
||
return [] | ||
|
||
|
||
def map_audit_log_record_to_grafana_annotation( | ||
audit_log_record: AuditLog, | ||
) -> GrafanaAnnotation: | ||
tags = [ | ||
"flagsmith", | ||
f"project:{audit_log_record.project_name}", | ||
f"environment:{audit_log_record.environment_name}", | ||
f"by:{audit_log_record.author_identifier}", | ||
*_get_instance_tags_from_audit_log_record(audit_log_record), | ||
] | ||
time = int(audit_log_record.created_date.timestamp() * 1000) # ms since epoch | ||
|
||
return { | ||
"tags": tags, | ||
"text": audit_log_record.log, | ||
"time": time, | ||
"timeEnd": time, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
from integrations.common.serializers import ( | ||
BaseEnvironmentIntegrationModelSerializer, | ||
BaseProjectIntegrationModelSerializer, | ||
) | ||
|
||
from .models import GrafanaConfiguration | ||
|
||
|
||
class GrafanaConfigurationSerializer(BaseEnvironmentIntegrationModelSerializer): | ||
class GrafanaConfigurationSerializer(BaseProjectIntegrationModelSerializer): | ||
class Meta: | ||
model = GrafanaConfiguration | ||
fields = ("id", "base_url", "api_key") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from typing import TypedDict | ||
|
||
|
||
class GrafanaAnnotation(TypedDict): | ||
tags: list[str] | ||
text: str | ||
time: int | ||
timeEnd: int |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
from integrations.common.views import EnvironmentIntegrationCommonViewSet | ||
from integrations.common.views import ProjectIntegrationBaseViewSet | ||
from integrations.grafana.models import GrafanaConfiguration | ||
from integrations.grafana.serializers import GrafanaConfigurationSerializer | ||
|
||
|
||
class GrafanaConfigurationViewSet(EnvironmentIntegrationCommonViewSet): | ||
class GrafanaConfigurationViewSet(ProjectIntegrationBaseViewSet): | ||
serializer_class = GrafanaConfigurationSerializer | ||
pagination_class = None # set here to ensure documentation is correct | ||
model_class = GrafanaConfiguration |
Oops, something went wrong.