From 32c08a09c3091fcb54213ae4208f5b414a23f95f Mon Sep 17 00:00:00 2001 From: Rigel Di Scala Date: Mon, 3 Aug 2020 22:32:36 +0200 Subject: [PATCH 1/2] Serialize Workflow Job Template inventories by natural key - related #7798 This changeset introduces two changes: 1. Update the API representation of Workflow Job Templates to use the natural key of the Inventory type instead of its id; 2. Override the related property of the CLI's WorkflowJobTemplate page type to patch the related references during the export process, allowing the resource to be serialised using the natural key of the Inventory type instead of the id. Change n.2 is a workaround that is used when exporting resources from AWX/Tower instances that don't have change n.1. It can be removed in the future. --- awx/api/serializers.py | 6 ++++++ .../api/pages/workflow_job_templates.py | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 2bb25f3de2..cf6be7ea37 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -3438,6 +3438,12 @@ class WorkflowJobTemplateSerializer(JobTemplateMixin, LabelsListMixin, UnifiedJo res['organization'] = self.reverse('api:organization_detail', kwargs={'pk': obj.organization.pk}) if obj.webhook_credential_id: res['webhook_credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.webhook_credential_id}) + if obj.inventory: + res['inventory'] = self.reverse( + 'api:inventory_detail', kwargs={ + 'pk': obj.inventory.pk + } + ) return res def validate_extra_vars(self, value): diff --git a/awxkit/awxkit/api/pages/workflow_job_templates.py b/awxkit/awxkit/api/pages/workflow_job_templates.py index 17f3b56342..c134196dca 100644 --- a/awxkit/awxkit/api/pages/workflow_job_templates.py +++ b/awxkit/awxkit/api/pages/workflow_job_templates.py @@ -15,6 +15,27 @@ class WorkflowJobTemplate(HasCopy, HasCreate, HasNotifications, HasSurvey, Unifi optional_dependencies = [Organization] NATURAL_KEY = ('organization', 'name') + @property + def related(self): + """Augment the related namespace with the inventory. + + This provides a workaround for API instances that do not provide + a reference to this workflow's associated inventory, if defined, + in the related namespace. + + See issue #7798. + """ + related_data = self.__getattr__('related') + if 'inventory' not in related_data: + inventory_id = self.json['inventory'] + if inventory_id: + endpoint_url = '/api/v2/inventories/{}/'.format(inventory_id) + related_data['inventory'] = page.TentativePage( + endpoint_url, + self.connection + ) + return related_data + def launch(self, payload={}): """Launch using related->launch endpoint.""" # get related->launch From a07dabae9e38094747cb6544e25cf9f71cae6735 Mon Sep 17 00:00:00 2001 From: Jeff Bradberry Date: Thu, 8 Oct 2020 11:23:17 -0400 Subject: [PATCH 2/2] Attempt to infer related pages on export when the link is missing --- awx/api/serializers.py | 4 ++-- awxkit/awxkit/api/pages/api.py | 17 ++++++++++++--- .../api/pages/workflow_job_templates.py | 21 ------------------- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index cf6be7ea37..2e70d2c8c9 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -3438,10 +3438,10 @@ class WorkflowJobTemplateSerializer(JobTemplateMixin, LabelsListMixin, UnifiedJo res['organization'] = self.reverse('api:organization_detail', kwargs={'pk': obj.organization.pk}) if obj.webhook_credential_id: res['webhook_credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.webhook_credential_id}) - if obj.inventory: + if obj.inventory_id: res['inventory'] = self.reverse( 'api:inventory_detail', kwargs={ - 'pk': obj.inventory.pk + 'pk': obj.inventory_id } ) return res diff --git a/awxkit/awxkit/api/pages/api.py b/awxkit/awxkit/api/pages/api.py index 664c0c18cf..a8759d1896 100644 --- a/awxkit/awxkit/api/pages/api.py +++ b/awxkit/awxkit/api/pages/api.py @@ -72,10 +72,21 @@ class ApiV2(base.Base): } for key in post_fields: - if key not in _page.related: - continue + if key in _page.related: + related = _page.related[key] + else: + if post_fields[key]['type'] == 'id' and _page.json.get(key) is not None: + log.warning("Related link %r missing from %s, attempting to reconstruct endpoint.", + key, _page.endpoint) + resource = getattr(self, key, None) + if resource is None: + log.error("Unable to infer endpoint for %r on %s.", key, _page.endpoint) + continue + related = self._filtered_list(resource, _page.json[key]).results[0] + else: + continue - rel_endpoint = self._cache.get_page(_page.related[key]) + rel_endpoint = self._cache.get_page(related) if rel_endpoint is None: # This foreign key is unreadable if post_fields[key].get('required'): log.error("Foreign key %r export failed for object %s.", key, _page.endpoint) diff --git a/awxkit/awxkit/api/pages/workflow_job_templates.py b/awxkit/awxkit/api/pages/workflow_job_templates.py index c134196dca..17f3b56342 100644 --- a/awxkit/awxkit/api/pages/workflow_job_templates.py +++ b/awxkit/awxkit/api/pages/workflow_job_templates.py @@ -15,27 +15,6 @@ class WorkflowJobTemplate(HasCopy, HasCreate, HasNotifications, HasSurvey, Unifi optional_dependencies = [Organization] NATURAL_KEY = ('organization', 'name') - @property - def related(self): - """Augment the related namespace with the inventory. - - This provides a workaround for API instances that do not provide - a reference to this workflow's associated inventory, if defined, - in the related namespace. - - See issue #7798. - """ - related_data = self.__getattr__('related') - if 'inventory' not in related_data: - inventory_id = self.json['inventory'] - if inventory_id: - endpoint_url = '/api/v2/inventories/{}/'.format(inventory_id) - related_data['inventory'] = page.TentativePage( - endpoint_url, - self.connection - ) - return related_data - def launch(self, payload={}): """Launch using related->launch endpoint.""" # get related->launch