From 250484339bf41ae696ec955f79efbfb85df89744 Mon Sep 17 00:00:00 2001 From: Jeff Bradberry Date: Fri, 10 May 2019 14:13:37 -0400 Subject: [PATCH 1/4] Expose the role's content_type field and reorder the objects in the UI when the resource was stored on ActivityStream as object1 instead of object2. related #3841 --- awx/api/serializers.py | 4 ++- .../factories/build-description.factory.js | 26 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 1a54312004..cb05e615dd 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -128,7 +128,7 @@ SUMMARIZABLE_FK_FIELDS = { 'inventory_source': ('source', 'last_updated', 'status'), 'custom_inventory_script': DEFAULT_SUMMARY_FIELDS, 'source_script': ('name', 'description'), - 'role': ('id', 'role_field'), + 'role': ('id', 'role_field', 'content_type'), 'notification_template': DEFAULT_SUMMARY_FIELDS, 'instance_group': {'id', 'name', 'controller_id'}, 'insights_credential': DEFAULT_SUMMARY_FIELDS, @@ -5106,6 +5106,8 @@ class ActivityStreamSerializer(BaseSerializer): for field in related_fields: fval = getattr(thisItem, field, None) if fval is not None: + if field == 'content_type': + fval = str(fval) thisItemDict[field] = fval summary_fields[fk].append(thisItemDict) except ObjectDoesNotExist: diff --git a/awx/ui/client/src/activity-stream/factories/build-description.factory.js b/awx/ui/client/src/activity-stream/factories/build-description.factory.js index 20fcb4c7b3..93c1e41593 100644 --- a/awx/ui/client/src/activity-stream/factories/build-description.factory.js +++ b/awx/ui/client/src/activity-stream/factories/build-description.factory.js @@ -17,6 +17,16 @@ export default function BuildDescription(BuildAnchor, $log, i18n) { switch(activity.object_association){ // explicit role dis+associations case 'role': + var object1 = activity.object1; + var object2 = activity.object2; + + // if object1 winds up being the role's resource, we need to swap the objects + // in order to make the sentence make sense. + if (activity.summary_fields.role[0].content_type === object1) { + object1 = activity.object2; + object2 = activity.object1; + } + // object1 field is resource targeted by the dis+association // object2 field is the resource the role is inherited from // summary_field.role[0] contains ref info about the role @@ -24,23 +34,23 @@ export default function BuildDescription(BuildAnchor, $log, i18n) { // expected outcome: "disassociated role_name from " case 'disassociate': if (isGroupRelationship(activity)){ - activity.description += BuildAnchor(activity.summary_fields.group[1], activity.object2, activity) + activity.summary_fields.role[0].role_field + - ' from ' + BuildAnchor(activity.summary_fields.group[0], activity.object1, activity); + activity.description += BuildAnchor(activity.summary_fields.group[1], object2, activity) + activity.summary_fields.role[0].role_field + + ' from ' + BuildAnchor(activity.summary_fields.group[0], object1, activity); } else{ - activity.description += BuildAnchor(activity.summary_fields[activity.object2][0], activity.object2, activity) + activity.summary_fields.role[0].role_field + - ' from ' + BuildAnchor(activity.summary_fields[activity.object1][0], activity.object1, activity); + activity.description += BuildAnchor(activity.summary_fields[object2][0], object2, activity) + activity.summary_fields.role[0].role_field + + ' from ' + BuildAnchor(activity.summary_fields[object1][0], object1, activity); } break; // expected outcome: "associated role_name to " case 'associate': if (isGroupRelationship(activity)){ - activity.description += BuildAnchor(activity.summary_fields.group[1], activity.object2, activity) + activity.summary_fields.role[0].role_field + - ' to ' + BuildAnchor(activity.summary_fields.group[0], activity.object1, activity); + activity.description += BuildAnchor(activity.summary_fields.group[1], object2, activity) + activity.summary_fields.role[0].role_field + + ' to ' + BuildAnchor(activity.summary_fields.group[0], object1, activity); } else{ - activity.description += BuildAnchor(activity.summary_fields[activity.object2][0], activity.object2, activity) + activity.summary_fields.role[0].role_field + - ' to ' + BuildAnchor(activity.summary_fields[activity.object1][0], activity.object1, activity); + activity.description += BuildAnchor(activity.summary_fields[object2][0], object2, activity) + activity.summary_fields.role[0].role_field + + ' to ' + BuildAnchor(activity.summary_fields[object1][0], object1, activity); } break; } From d6e89092d32c6102c8e666af717070569837cdc3 Mon Sep 17 00:00:00 2001 From: Jeff Bradberry Date: Fri, 10 May 2019 17:31:33 -0400 Subject: [PATCH 2/4] Instead of exposing Role.content_type, create a new serializer field called `object_type`, which is constructed based on manipulating the string value of ActivityStream.object_relationship_type. Since that field does have the full class name, this manipulation should match the manipulation that is done to construct the values of object1 and object2 when ActivityStream is created. --- awx/api/serializers.py | 24 +++++++++++++++---- .../factories/build-description.factory.js | 2 +- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index cb05e615dd..5899b9af77 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -128,7 +128,7 @@ SUMMARIZABLE_FK_FIELDS = { 'inventory_source': ('source', 'last_updated', 'status'), 'custom_inventory_script': DEFAULT_SUMMARY_FIELDS, 'source_script': ('name', 'description'), - 'role': ('id', 'role_field', 'content_type'), + 'role': ('id', 'role_field'), 'notification_template': DEFAULT_SUMMARY_FIELDS, 'instance_group': {'id', 'name', 'controller_id'}, 'insights_credential': DEFAULT_SUMMARY_FIELDS, @@ -4956,6 +4956,7 @@ class ActivityStreamSerializer(BaseSerializer): changes = serializers.SerializerMethodField() object_association = serializers.SerializerMethodField() + object_type = serializers.SerializerMethodField() @cached_property def _local_summarizable_fk_fields(self): @@ -4980,8 +4981,8 @@ class ActivityStreamSerializer(BaseSerializer): class Meta: model = ActivityStream - fields = ('*', '-name', '-description', '-created', '-modified', - 'timestamp', 'operation', 'changes', 'object1', 'object2', 'object_association') + fields = ('*', '-name', '-description', '-created', '-modified', 'timestamp', 'operation', + 'changes', 'object1', 'object2', 'object_association', 'object_type') def get_fields(self): ret = super(ActivityStreamSerializer, self).get_fields() @@ -5024,6 +5025,21 @@ class ActivityStreamSerializer(BaseSerializer): logger.debug('Failed to parse activity stream relationship type {}'.format(obj.object_relationship_type)) return "" + def get_object_type(self, obj): + if not obj.object_relationship_type: + return "" + elif obj.object_relationship_type.endswith('_role'): + return camelcase_to_underscore(obj.object_relationship_type.rsplit('.', 2)[-2]) + # default case: these values look like + # "awx.main.models.organization.Organization_notification_templates_success" + # so we have to take after the last period but before the first underscore. + try: + cls = obj.object_relationship_type.rsplit('.', 1)[0] + return camelcase_to_underscore(cls.split('_', 1)) + except Exception: + logger.debug('Failed to parse activity stream relationship type {}'.format(obj.object_relationship_type)) + return "" + def get_related(self, obj): rel = {} if obj.actor is not None: @@ -5106,8 +5122,6 @@ class ActivityStreamSerializer(BaseSerializer): for field in related_fields: fval = getattr(thisItem, field, None) if fval is not None: - if field == 'content_type': - fval = str(fval) thisItemDict[field] = fval summary_fields[fk].append(thisItemDict) except ObjectDoesNotExist: diff --git a/awx/ui/client/src/activity-stream/factories/build-description.factory.js b/awx/ui/client/src/activity-stream/factories/build-description.factory.js index 93c1e41593..ddb05b0662 100644 --- a/awx/ui/client/src/activity-stream/factories/build-description.factory.js +++ b/awx/ui/client/src/activity-stream/factories/build-description.factory.js @@ -22,7 +22,7 @@ export default function BuildDescription(BuildAnchor, $log, i18n) { // if object1 winds up being the role's resource, we need to swap the objects // in order to make the sentence make sense. - if (activity.summary_fields.role[0].content_type === object1) { + if (activity.object_type === object1) { object1 = activity.object2; object2 = activity.object1; } From 05cae23180b93c5ffca1facaf83170b5d0c0ad5b Mon Sep 17 00:00:00 2001 From: Jeff Bradberry Date: Mon, 13 May 2019 11:10:33 -0400 Subject: [PATCH 3/4] Add help text to the ActivityStreamSerializer m2m relationship fields --- awx/api/serializers.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 5899b9af77..28187ee172 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4955,8 +4955,10 @@ class InstanceGroupSerializer(BaseSerializer): class ActivityStreamSerializer(BaseSerializer): changes = serializers.SerializerMethodField() - object_association = serializers.SerializerMethodField() - object_type = serializers.SerializerMethodField() + object_association = serializers.SerializerMethodField( + help_text="When present, shows the field name of the role or relationship that changed.") + object_type = serializers.SerializerMethodField( + help_text="When present, shows the model on which the role or relationship was defined.") @cached_property def _local_summarizable_fk_fields(self): From 3579584ffcb43bf3bb8f021880ab4a14a83034bd Mon Sep 17 00:00:00 2001 From: Jeff Bradberry Date: Mon, 13 May 2019 11:20:15 -0400 Subject: [PATCH 4/4] i18n --- awx/api/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 28187ee172..8b38895414 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4956,9 +4956,9 @@ class ActivityStreamSerializer(BaseSerializer): changes = serializers.SerializerMethodField() object_association = serializers.SerializerMethodField( - help_text="When present, shows the field name of the role or relationship that changed.") + help_text=_("When present, shows the field name of the role or relationship that changed.")) object_type = serializers.SerializerMethodField( - help_text="When present, shows the model on which the role or relationship was defined.") + help_text=_("When present, shows the model on which the role or relationship was defined.")) @cached_property def _local_summarizable_fk_fields(self):