Enable deletion of orphaned approval nodes

Update serializer to include workflow approval for activity stream
This commit is contained in:
beeankha
2019-07-25 15:03:26 -04:00
committed by Ryan Petrello
parent 64c94d478d
commit 3357c96774
7 changed files with 61 additions and 46 deletions

View File

@@ -3456,13 +3456,13 @@ class WorkflowApprovalTemplateSerializer(UnifiedJobTemplateSerializer):
return res return res
# class WorkflowJobTemplateApprovalSerializer(UnifiedJobTemplateSerializer): class WorkflowJobTemplateApprovalSerializer(UnifiedJobTemplateSerializer):
# class Meta: class Meta:
# model = WorkflowJobTemplateApproval model = WorkflowApprovalTemplate
# fields = ('*',) fields = ('*',)
#
# def post(self, obj): def post(self, obj):
# return # POST only!!! return # POST only!!!
class LaunchConfigurationBaseSerializer(BaseSerializer): class LaunchConfigurationBaseSerializer(BaseSerializer):
@@ -4746,7 +4746,8 @@ class ActivityStreamSerializer(BaseSerializer):
('o_auth2_access_token', ('id', 'user_id', 'description', 'application_id', 'scope')), ('o_auth2_access_token', ('id', 'user_id', 'description', 'application_id', 'scope')),
('o_auth2_application', ('id', 'name', 'description')), ('o_auth2_application', ('id', 'name', 'description')),
('credential_type', ('id', 'name', 'description', 'kind', 'managed_by_tower')), ('credential_type', ('id', 'name', 'description', 'kind', 'managed_by_tower')),
('ad_hoc_command', ('id', 'name', 'status', 'limit')) ('ad_hoc_command', ('id', 'name', 'status', 'limit')),
('workflow_approval', ('id', 'unified_job_id')),
] ]
return field_list return field_list
@@ -4855,6 +4856,7 @@ class ActivityStreamSerializer(BaseSerializer):
def _summarize_parent_ujt(self, obj, fk, summary_fields): def _summarize_parent_ujt(self, obj, fk, summary_fields):
summary_keys = {'job': 'job_template', summary_keys = {'job': 'job_template',
'workflow_job_template_node': 'workflow_job_template', 'workflow_job_template_node': 'workflow_job_template',
'workflow_approval': 'workflow_approval_template',
'schedule': 'unified_job_template'} 'schedule': 'unified_job_template'}
if fk not in summary_keys: if fk not in summary_keys:
return return

View File

@@ -839,8 +839,6 @@ class SystemJobEventsList(SubListAPIView):
return super(SystemJobEventsList, self).finalize_response(request, response, *args, **kwargs) return super(SystemJobEventsList, self).finalize_response(request, response, *args, **kwargs)
class ProjectUpdateCancel(RetrieveAPIView): class ProjectUpdateCancel(RetrieveAPIView):
model = models.ProjectUpdate model = models.ProjectUpdate

View File

@@ -2631,6 +2631,7 @@ class ActivityStreamAccess(BaseAccess):
app_set = OAuth2ApplicationAccess(self.user).filtered_queryset() app_set = OAuth2ApplicationAccess(self.user).filtered_queryset()
token_set = OAuth2TokenAccess(self.user).filtered_queryset() token_set = OAuth2TokenAccess(self.user).filtered_queryset()
# &&&&&& Activity Stream + RBAC here??
return qs.filter( return qs.filter(
Q(ad_hoc_command__inventory__in=inventory_set) | Q(ad_hoc_command__inventory__in=inventory_set) |
Q(o_auth2_application__in=app_set) | Q(o_auth2_application__in=app_set) |
@@ -2796,11 +2797,11 @@ class WorkflowApprovalAccess(BaseAccess):
self.user, 'read_role')) self.user, 'read_role'))
def get_queryset(self): def get_queryset(self):
return super(UnifiedJobTemplateAccess, self).get_queryset().exclude( return super(WorkflowApprovalAccess, self).get_queryset().exclude(
workflowapprovaltemplate__isnull=False) workflow_approval_template__isnull=False)
def can_approve_or_deny(self, obj): def can_approve_or_deny(self, obj):
if self.user.approval_role: if self.user.approval_role or self.user.system_administrator:
return True return True
@@ -2828,8 +2829,8 @@ class WorkflowApprovalTemplateAccess(BaseAccess):
self.user, 'read_role')) self.user, 'read_role'))
def get_queryset(self): def get_queryset(self):
return super(UnifiedJobAccess, self).get_queryset().exclude( return super(WorkflowApprovalTemplateAccess, self).get_queryset().filter(
workflowapproval__isnull=False) approvals__isnull=False)
for cls in BaseAccess.__subclasses__(): for cls in BaseAccess.__subclasses__():

View File

@@ -1,4 +1,4 @@
# Generated by Django 2.2.2 on 2019-07-18 14:12 # Generated by Django 2.2.2 on 2019-07-25 19:16
import awx.main.fields import awx.main.fields
from django.db import migrations, models from django.db import migrations, models
@@ -8,7 +8,7 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('main', '0082_v360_workflowapproval'), ('main', '0082_v360_webhook_http_method'),
] ]
operations = [ operations = [
@@ -32,6 +32,16 @@ class Migration(migrations.Migration):
field=awx.main.fields.ImplicitRoleField(editable=False, null='True', on_delete=django.db.models.deletion.CASCADE, parent_role=['singleton:system_auditor', 'organization.approval_role', 'admin_role'], related_name='+', to='main.Role'), field=awx.main.fields.ImplicitRoleField(editable=False, null='True', on_delete=django.db.models.deletion.CASCADE, parent_role=['singleton:system_auditor', 'organization.approval_role', 'admin_role'], related_name='+', to='main.Role'),
preserve_default='True', preserve_default='True',
), ),
migrations.AlterField(
model_name='workflowjobnode',
name='unified_job_template',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workflowjobnodes', to='main.UnifiedJobTemplate'),
),
migrations.AlterField(
model_name='workflowjobtemplatenode',
name='unified_job_template',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='workflowjobtemplatenodes', to='main.UnifiedJobTemplate'),
),
migrations.CreateModel( migrations.CreateModel(
name='WorkflowApproval', name='WorkflowApproval',
fields=[ fields=[
@@ -40,4 +50,14 @@ class Migration(migrations.Migration):
], ],
bases=('main.unifiedjob',), bases=('main.unifiedjob',),
), ),
migrations.AddField(
model_name='activitystream',
name='workflow_approval',
field=models.ManyToManyField(blank=True, to='main.WorkflowApproval'),
),
migrations.AddField(
model_name='activitystream',
name='workflow_approval_template',
field=models.ManyToManyField(blank=True, to='main.WorkflowApprovalTemplate'),
),
] ]

View File

@@ -203,7 +203,7 @@ activity_stream_registrar.connect(WorkflowJobTemplate)
activity_stream_registrar.connect(WorkflowJobTemplateNode) activity_stream_registrar.connect(WorkflowJobTemplateNode)
activity_stream_registrar.connect(WorkflowJob) activity_stream_registrar.connect(WorkflowJob)
activity_stream_registrar.connect(WorkflowApproval) activity_stream_registrar.connect(WorkflowApproval)
activity_stream_registrar.connect(WorkflowApprovalTemplate) # activity_stream_registrar.connect(WorkflowApprovalTemplate)
activity_stream_registrar.connect(OAuth2Application) activity_stream_registrar.connect(OAuth2Application)
activity_stream_registrar.connect(OAuth2AccessToken) activity_stream_registrar.connect(OAuth2AccessToken)

View File

@@ -66,9 +66,8 @@ class ActivityStream(models.Model):
workflow_job_node = models.ManyToManyField("WorkflowJobNode", blank=True) workflow_job_node = models.ManyToManyField("WorkflowJobNode", blank=True)
workflow_job_template = models.ManyToManyField("WorkflowJobTemplate", blank=True) workflow_job_template = models.ManyToManyField("WorkflowJobTemplate", blank=True)
workflow_job = models.ManyToManyField("WorkflowJob", blank=True) workflow_job = models.ManyToManyField("WorkflowJob", blank=True)
# Possibly adding workflow_approval-related fields here?? &&&&&& workflow_approval_template = models.ManyToManyField("WorkflowApprovalTemplate", blank=True)
# workflow_approval_template = models.ManyToManyField("WorkflowApprovalTemplate", blank=True) workflow_approval = models.ManyToManyField("WorkflowApproval", blank=True)
# workflow_approval = models.ManyToManyField("WorkflowApproval", blank=True)
unified_job_template = models.ManyToManyField("UnifiedJobTemplate", blank=True, related_name='activity_stream_as_unified_job_template+') unified_job_template = models.ManyToManyField("UnifiedJobTemplate", blank=True, related_name='activity_stream_as_unified_job_template+')
unified_job = models.ManyToManyField("UnifiedJob", blank=True, related_name='activity_stream_as_unified_job+') unified_job = models.ManyToManyField("UnifiedJob", blank=True, related_name='activity_stream_as_unified_job+')
ad_hoc_command = models.ManyToManyField("AdHocCommand", blank=True) ad_hoc_command = models.ManyToManyField("AdHocCommand", blank=True)

View File

@@ -34,8 +34,8 @@ from awx.main.models import (
InventorySource, InventoryUpdateEvent, Job, JobEvent, JobHostSummary, InventorySource, InventoryUpdateEvent, Job, JobEvent, JobHostSummary,
JobTemplate, OAuth2AccessToken, Organization, Project, ProjectUpdateEvent, JobTemplate, OAuth2AccessToken, Organization, Project, ProjectUpdateEvent,
Role, SystemJob, SystemJobEvent, SystemJobTemplate, UnifiedJob, Role, SystemJob, SystemJobEvent, SystemJobTemplate, UnifiedJob,
UnifiedJobTemplate, User, UserSessionMembership, UnifiedJobTemplate, User, UserSessionMembership, WorkflowJobTemplateNode,
ROLE_SINGLETON_SYSTEM_ADMINISTRATOR WorkflowApprovalTemplate, ROLE_SINGLETON_SYSTEM_ADMINISTRATOR
) )
from awx.main.constants import CENSOR_VALUE from awx.main.constants import CENSOR_VALUE
from awx.main.utils import model_instance_diff, model_to_dict, camelcase_to_underscore, get_current_apps from awx.main.utils import model_instance_diff, model_to_dict, camelcase_to_underscore, get_current_apps
@@ -431,7 +431,7 @@ def model_serializer_mapping():
models.WorkflowJobTemplate: serializers.WorkflowJobTemplateWithSpecSerializer, models.WorkflowJobTemplate: serializers.WorkflowJobTemplateWithSpecSerializer,
models.WorkflowJobTemplateNode: serializers.WorkflowJobTemplateNodeSerializer, models.WorkflowJobTemplateNode: serializers.WorkflowJobTemplateNodeSerializer,
models.WorkflowApproval: serializers.WorkflowApprovalSerializer, models.WorkflowApproval: serializers.WorkflowApprovalSerializer,
models.WorkflowApprovalTemplate: serializers.WorkflowApprovalTemplateSerializer, # &&&&&& models.WorkflowApprovalTemplate: serializers.WorkflowApprovalTemplateSerializer,
models.WorkflowJob: serializers.WorkflowJobSerializer, models.WorkflowJob: serializers.WorkflowJobSerializer,
models.OAuth2AccessToken: serializers.OAuth2TokenSerializer, models.OAuth2AccessToken: serializers.OAuth2TokenSerializer,
models.OAuth2Application: serializers.OAuth2ApplicationSerializer, models.OAuth2Application: serializers.OAuth2ApplicationSerializer,
@@ -506,11 +506,6 @@ def activity_stream_update(sender, instance, **kwargs):
activity_entry.setting = conf_to_dict(instance) activity_entry.setting = conf_to_dict(instance)
activity_entry.save() activity_entry.save()
# &&&&&&
# if isinstance(obj1, WorkflowApprovalTemplate) or isinstance(obj2_actual, WorkflowApprovalTemplate):
# continue
def activity_stream_delete(sender, instance, **kwargs): def activity_stream_delete(sender, instance, **kwargs):
if not activity_stream_enabled: if not activity_stream_enabled:
@@ -645,23 +640,23 @@ def delete_inventory_for_org(sender, instance, **kwargs):
logger.debug(e) logger.debug(e)
# &&&&&& Placeholder code below for approval node deletion. @receiver(pre_delete, sender=WorkflowJobTemplateNode)
# @receiver(pre_delete, sender=Job) def delete_approval_nodes(sender, instance, **kwargs):
# def delete_detached_approval_nodes(sender, instance, **kwargs): if type(instance.unified_job_template) is WorkflowApprovalTemplate:
# for l in instance.labels.all(): instance.unified_job_template.delete()
# if l.is_candidate_for_detach():
# l.delete()
# # When setting UJT to anything other than "is approval node" - update this comment!
# @receiver(pre_save, sender=WorkflowJobTemplateNode)
# @receiver(pre_delete, sender=Organization) def placeholder_name(sender, instance, **kwargs):
# def delete_detached_approval_nodes(sender, instance, **kwargs): try:
# approval_node = ??? old = WorkflowJobTemplateNode.objects.get(id=instance.id)
# user = get_current_user_or_none() except sender.DoesNotExist:
# for node in approval_node: return
# try: if old.unified_job_template == instance.unified_job_template:
# node.schedule_deletion(user_id=getattr(user, 'id', None)) return
# except RuntimeError as e: if type(old.unified_job_template) is WorkflowApprovalTemplate:
# logger.debug(e) old.unified_job_template.delete()
@receiver(post_save, sender=Session) @receiver(post_save, sender=Session)