From 2712643c62a18e09272fa6cf9377c3aa443b4c28 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Thu, 23 Jan 2014 15:44:30 -0500 Subject: [PATCH] Implement AC-667... user serializer to determine read-only fields and use those to decide what to populate in activity stream create instances AND when to create active stream update instances --- awx/main/signals.py | 19 +++++++++++++++++-- awx/main/utils.py | 36 ++++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/awx/main/signals.py b/awx/main/signals.py index 499612ea07..91a0cb2da4 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -13,6 +13,7 @@ from django.dispatch import receiver # AWX from awx.main.models import * +from awx.api.serializers import * from awx.main.utils import model_instance_diff, model_to_dict, camelcase_to_underscore __all__ = [] @@ -186,6 +187,18 @@ def update_host_last_job_after_job_deleted(sender, **kwargs): # Set via ActivityStreamRegistrar to record activity stream events +model_serializer_mapping = {Organization: OrganizationSerializer, + Inventory: InventorySerializer, + Host: HostSerializer, + Group: GroupSerializer, + InventorySource: InventorySourceSerializer, + Credential: CredentialSerializer, + Team: TeamSerializer, + Project: ProjectSerializer, + Permission: PermissionSerializer, + JobTemplate: JobTemplateSerializer, + Job: JobSerializer} + def activity_stream_create(sender, instance, created, **kwargs): if created: # TODO: Rethink details of the new instance @@ -193,7 +206,7 @@ def activity_stream_create(sender, instance, created, **kwargs): activity_entry = ActivityStream( operation='create', object1=object1, - changes=json.dumps(model_to_dict(instance))) + changes=json.dumps(model_to_dict(instance, model_serializer_mapping))) activity_entry.save() getattr(activity_entry, object1).add(instance) @@ -209,7 +222,9 @@ def activity_stream_update(sender, instance, **kwargs): return new = instance - changes = model_instance_diff(old, new) + changes = model_instance_diff(old, new, model_serializer_mapping) + if changes is None: + return object1 = camelcase_to_underscore(instance.__class__.__name__) activity_entry = ActivityStream( operation='update', diff --git a/awx/main/utils.py b/awx/main/utils.py index 6c72dc6608..a05b82a422 100644 --- a/awx/main/utils.py +++ b/awx/main/utils.py @@ -225,10 +225,12 @@ def update_scm_url(scm_type, url, username=True, password=True, parts.query, parts.fragment]) return new_url -def model_instance_diff(old, new): +def model_instance_diff(old, new, serializer_mapping=None): """ Calculate the differences between two model instances. One of the instances may be None (i.e., a newly created model or deleted model). This will cause all fields with a value to have changed (from None). + serializer_mapping are used to determine read-only fields. + When provided, read-only fields will not be included in the resulting dictionary """ from django.db.models import Model from awx.main.models.organization import Credential @@ -249,28 +251,42 @@ def model_instance_diff(old, new): else: fields = set() - for field in fields: - old_value = str(getattr(old, field.name, None)) - new_value = str(getattr(new, field.name, None)) + if serializer_mapping is not None and new.__class__ in serializer_mapping: + serializer_actual = serializer_mapping[new.__class__]() + allowed_fields = [x for x in serializer_actual.fields if not serializer_actual.fields[x].read_only] + ['id'] + else: + allowed_fields = [x.name for x in new._meta.fields] - if old_value != new_value and field.name not in Credential.PASSWORD_FIELDS: - diff[field.name] = (old_value, new_value) - elif field.name in Credential.PASSWORD_FIELDS: - diff[field.name] = ("hidden", "hidden") + for field in allowed_fields: + old_value = str(getattr(old, field, None)) + new_value = str(getattr(new, field, None)) + + if old_value != new_value and field not in Credential.PASSWORD_FIELDS: + diff[field] = (old_value, new_value) + elif old_value != new_value and field in Credential.PASSWORD_FIELDS: + diff[field] = ("hidden", "hidden") if len(diff) == 0: diff = None return diff -def model_to_dict(obj): +def model_to_dict(obj, serializer_mapping=None): """ Serialize a model instance to a dictionary as best as possible + serializer_mapping are used to determine read-only fields. + When provided, read-only fields will not be included in the resulting dictionary """ from awx.main.models.organization import Credential attr_d = {} + if serializer_mapping is not None and obj.__class__ in serializer_mapping: + serializer_actual = serializer_mapping[obj.__class__]() + allowed_fields = [x for x in serializer_actual.fields if not serializer_actual.fields[x].read_only] + ['id'] + else: + allowed_fields = [x.name for x in obj._meta.fields] for field in obj._meta.fields: - # FIXME: This needs to be aware of fields not to be included in the AS delta log + if field.name not in allowed_fields: + continue if field.name not in Credential.PASSWORD_FIELDS: attr_d[field.name] = str(getattr(obj, field.name, None)) else: