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: explicitly set audit log created date #3083

Merged
merged 3 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.23 on 2023-12-01 10:47

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('audit', '0012_auto_20230517_1006'),
]

operations = [
migrations.AlterField(
model_name='auditlog',
name='created_date',
field=models.DateTimeField(verbose_name='DateCreated'),
),
]
8 changes: 7 additions & 1 deletion api/audit/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from django.db import models
from django.db.models import Model, Q
from django.utils import timezone
from django_lifecycle import (
AFTER_CREATE,
BEFORE_CREATE,
Expand All @@ -19,7 +20,7 @@


class AuditLog(LifecycleModel):
created_date = models.DateTimeField("DateCreated", auto_now_add=True)
created_date = models.DateTimeField("DateCreated")

project = models.ForeignKey(
Project, related_name="audit_logs", null=True, on_delete=models.DO_NOTHING
Expand Down Expand Up @@ -103,6 +104,11 @@ def add_project(self):
if self.environment and self.project is None:
self.project = self.environment.project

@hook(BEFORE_CREATE)
def add_created_date(self) -> None:
if not self.created_date:
self.created_date = timezone.now()

@hook(
AFTER_CREATE,
priority=priority.HIGHEST_PRIORITY,
Expand Down
8 changes: 8 additions & 0 deletions api/audit/tasks.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import logging
import typing
from datetime import datetime

from django.contrib.auth import get_user_model
from django.utils import timezone

from audit.constants import (
FEATURE_STATE_UPDATED_BY_CHANGE_REQUEST_MESSAGE,
Expand Down Expand Up @@ -55,6 +57,7 @@ def _create_feature_state_audit_log_for_change_request(
project=feature_state.environment.project,
log=log,
is_system_event=True,
created_date=feature_state.live_from,
)


Expand Down Expand Up @@ -113,6 +116,7 @@ def create_audit_log_from_historical_record(
related_object_type=related_object_type.name,
log=log_message,
master_api_key=history_instance.master_api_key,
created_date=history_instance.history_date,
**instance.get_extra_audit_log_kwargs(history_instance),
)

Expand All @@ -123,6 +127,7 @@ def create_segment_priorities_changed_audit_log(
feature_segment_ids: typing.List[int],
user_id: int = None,
master_api_key_id: int = None,
changed_at: str = None,
):
"""
This needs to be a separate task called by the view itself. This is because the OrderedModelBase class
Expand Down Expand Up @@ -166,4 +171,7 @@ def create_segment_priorities_changed_audit_log(
related_object_id=feature.id,
related_object_type=RelatedObjectType.FEATURE.name,
master_api_key_id=master_api_key_id,
created_date=datetime.fromisoformat(changed_at)
if changed_at is not None
else timezone.now(),
)
3 changes: 3 additions & 0 deletions api/edge_api/identities/tasks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import logging
import typing

from django.utils import timezone

from audit.models import AuditLog
from audit.related_object_type import RelatedObjectType
from environments.models import Environment, Webhook
Expand Down Expand Up @@ -123,6 +125,7 @@ def generate_audit_log_records(
related_object_type=RelatedObjectType.EDGE_IDENTITY.name,
related_object_uuid=identity_uuid,
master_api_key_id=master_api_key_id,
created_date=timezone.now(),
)
)

Expand Down
1 change: 1 addition & 0 deletions api/features/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ def sort_function(id_priority_pair):
"master_api_key_id": request.master_api_key.id
if hasattr(request, "master_api_key")
else None,
"changed_at": timezone.now().isoformat(),
}
)

Expand Down
41 changes: 32 additions & 9 deletions api/tests/unit/audit/test_unit_audit_tasks.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from django.utils import timezone

from audit.constants import (
FEATURE_STATE_UPDATED_BY_CHANGE_REQUEST_MESSAGE,
FEATURE_STATE_WENT_LIVE_MESSAGE,
Expand All @@ -11,8 +13,9 @@
create_segment_priorities_changed_audit_log,
)
from environments.models import Environment
from features.models import FeatureSegment
from features.models import Feature, FeatureSegment, FeatureState
from segments.models import Segment
from users.models import FFAdminUser


def test_create_audit_log_from_historical_record_does_nothing_if_no_user_or_api_key(
Expand Down Expand Up @@ -163,7 +166,11 @@ def test_create_audit_log_from_historical_record_creates_audit_log_with_correct_
instance.get_audit_log_related_object_type.return_value = related_object_type
instance.get_extra_audit_log_kwargs.return_value = {}
history_instance = mocker.MagicMock(
history_id=1, instance=instance, master_api_key=None, history_type="+"
history_id=1,
instance=instance,
master_api_key=None,
history_type="+",
history_date=timezone.now(),
)

history_user = mocker.MagicMock()
Expand Down Expand Up @@ -205,12 +212,16 @@ def test_create_audit_log_from_historical_record_creates_audit_log_with_correct_
related_object_type=related_object_type.name,
log=log_message,
master_api_key=None,
created_date=history_instance.history_date,
)


def test_create_segment_priorities_changed_audit_log(
admin_user, feature_segment, feature, environment
):
admin_user: FFAdminUser,
feature_segment: FeatureSegment,
feature: Feature,
environment: Environment,
) -> None:
# Given
another_segment = Segment.objects.create(
project=environment.project, name="Another Segment"
Expand All @@ -219,6 +230,8 @@ def test_create_segment_priorities_changed_audit_log(
feature=feature, environment=environment, segment=another_segment
)

now = timezone.now()

# When
create_segment_priorities_changed_audit_log(
previous_id_priority_pairs=[
Expand All @@ -227,16 +240,20 @@ def test_create_segment_priorities_changed_audit_log(
],
feature_segment_ids=[feature_segment.id, another_feature_segment.id],
user_id=admin_user.id,
changed_at=now.isoformat(),
)

# Then
assert AuditLog.objects.filter(
environment=environment,
log=f"Segment overrides re-ordered for feature '{feature.name}'.",
created_date=now,
).exists()


def test_create_feature_state_went_live_audit_log(change_request_feature_state):
def test_create_feature_state_went_live_audit_log(
change_request_feature_state: FeatureState,
) -> None:
# Given
message = FEATURE_STATE_WENT_LIVE_MESSAGE % (
change_request_feature_state.feature.name,
Expand All @@ -250,15 +267,18 @@ def test_create_feature_state_went_live_audit_log(change_request_feature_state):
# Then
assert (
AuditLog.objects.filter(
related_object_id=feature_state_id, is_system_event=True, log=message
related_object_id=feature_state_id,
is_system_event=True,
log=message,
created_date=change_request_feature_state.live_from,
).count()
== 1
)


def test_create_feature_state_updated_by_change_request_audit_log(
change_request_feature_state,
):
change_request_feature_state: FeatureState,
) -> None:
# Given
message = FEATURE_STATE_UPDATED_BY_CHANGE_REQUEST_MESSAGE % (
change_request_feature_state.feature.name,
Expand All @@ -272,7 +292,10 @@ def test_create_feature_state_updated_by_change_request_audit_log(
# Then
assert (
AuditLog.objects.filter(
related_object_id=feature_state_id, is_system_event=True, log=message
related_object_id=feature_state_id,
is_system_event=True,
log=message,
created_date=change_request_feature_state.live_from,
).count()
== 1
)
Expand Down
9 changes: 6 additions & 3 deletions api/tests/unit/features/test_unit_features_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import timedelta
from unittest import mock

import freezegun
import pytest
from django.core.exceptions import ValidationError
from django.db.utils import IntegrityError
Expand Down Expand Up @@ -848,9 +849,10 @@ def test_feature_segment_update_priorities_when_changes(
new_id_priority_pairs = [(feature_segment.id, 1), (another_feature_segment.id, 0)]

# When
returned_feature_segments = FeatureSegment.update_priorities(
new_feature_segment_id_priorities=new_id_priority_pairs
)
with freezegun.freeze_time(now):
returned_feature_segments = FeatureSegment.update_priorities(
new_feature_segment_id_priorities=new_id_priority_pairs
)

# Then
assert sorted(
Expand All @@ -864,6 +866,7 @@ def test_feature_segment_update_priorities_when_changes(
"feature_segment_ids": [feature_segment.id, another_feature_segment.id],
"user_id": mocked_request.user.id,
"master_api_key_id": mocked_request.master_api_key.id,
"changed_at": now.isoformat(),
}
)

Expand Down