mirror of
https://github.com/ansible/awx.git
synced 2026-03-04 10:11:05 -03:30
Remove the natural key lookup dict and make each Page responsible instead
This commit is contained in:
@@ -35,50 +35,6 @@ EXPORTABLE_DEPENDENT_OBJECTS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
NATURAL_KEYS = {
|
|
||||||
'user': ('username',),
|
|
||||||
'organization': ('name',),
|
|
||||||
'team': ('organization', 'name'),
|
|
||||||
'credential_type': ('name', 'kind'),
|
|
||||||
'credential': ('organization', 'name', 'credential_type'),
|
|
||||||
'notification_template': ('organization', 'name'),
|
|
||||||
'project': ('organization', 'name'),
|
|
||||||
'inventory': ('organization', 'name'),
|
|
||||||
'job_template': ('organization', 'name'),
|
|
||||||
'workflow_job_template': ('organization', 'name'),
|
|
||||||
|
|
||||||
# related resources
|
|
||||||
'role': ('name', ':content_object'),
|
|
||||||
'notification_template': ('organization', 'name'),
|
|
||||||
'label': ('organization', 'name'), # FIXME: label will need to be fully constructed from this
|
|
||||||
'workflow_job_template_node': ('workflow_job_template', 'identifier'),
|
|
||||||
'schedule': ('unified_job_template', 'name'),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_natural_key(pg):
|
|
||||||
natural_key = {'type': pg['type']}
|
|
||||||
lookup = NATURAL_KEYS.get(pg['type'], ())
|
|
||||||
|
|
||||||
for key in lookup or ():
|
|
||||||
if key.startswith(':'):
|
|
||||||
# treat it like a special-case related object
|
|
||||||
related_objs = [
|
|
||||||
related for name, related in pg.related.items()
|
|
||||||
if name not in ('users', 'teams')
|
|
||||||
]
|
|
||||||
if related_objs:
|
|
||||||
natural_key[key[1:]] = get_natural_key(related_objs[0].get())
|
|
||||||
elif key in pg.related:
|
|
||||||
natural_key[key] = get_natural_key(pg.related[key].get())
|
|
||||||
elif key in pg:
|
|
||||||
natural_key[key] = pg[key]
|
|
||||||
|
|
||||||
if not natural_key:
|
|
||||||
return None
|
|
||||||
return natural_key
|
|
||||||
|
|
||||||
|
|
||||||
def freeze(key):
|
def freeze(key):
|
||||||
if key is None:
|
if key is None:
|
||||||
return None
|
return None
|
||||||
@@ -113,10 +69,11 @@ class ApiV2(base.Base):
|
|||||||
key: asset.json[key] for key in options
|
key: asset.json[key] for key in options
|
||||||
if key in asset.json and key not in asset.related and key != 'id'
|
if key in asset.json and key not in asset.related and key != 'id'
|
||||||
}
|
}
|
||||||
fields['natural_key'] = get_natural_key(asset)
|
fields['natural_key'] = asset.get_natural_key()
|
||||||
|
|
||||||
fk_fields = {
|
fk_fields = {
|
||||||
key: get_natural_key(asset.related[key].get()) for key in options
|
# FIXME: use caching by url
|
||||||
|
key: asset.related[key].get().get_natural_key() for key in options
|
||||||
if key in asset.related
|
if key in asset.related
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +81,7 @@ class ApiV2(base.Base):
|
|||||||
for key, related_endpoint in asset.related.items():
|
for key, related_endpoint in asset.related.items():
|
||||||
if key in asset.json or not related_endpoint:
|
if key in asset.json or not related_endpoint:
|
||||||
continue
|
continue
|
||||||
if key == 'object_roles':
|
if key == 'object_roles': # FIXME
|
||||||
continue
|
continue
|
||||||
rel = related_endpoint._create()
|
rel = related_endpoint._create()
|
||||||
|
|
||||||
@@ -139,7 +96,7 @@ class ApiV2(base.Base):
|
|||||||
if 'results' in data:
|
if 'results' in data:
|
||||||
related_options = self._get_options(related_endpoint)
|
related_options = self._get_options(related_endpoint)
|
||||||
related[key] = [
|
related[key] = [
|
||||||
get_natural_key(x) if by_natural_key else self._serialize_asset(x, related_options)
|
x.get_natural_key() if by_natural_key else self._serialize_asset(x, related_options)
|
||||||
for x in data.results
|
for x in data.results
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
@@ -190,7 +147,7 @@ class ApiV2(base.Base):
|
|||||||
yield page_resource[page_cls]
|
yield page_resource[page_cls]
|
||||||
|
|
||||||
def _register_page(self, page):
|
def _register_page(self, page):
|
||||||
natural_key = freeze(get_natural_key(page))
|
natural_key = freeze(page.get_natural_key())
|
||||||
# FIXME: we need to keep a reference for the case where we
|
# FIXME: we need to keep a reference for the case where we
|
||||||
# don't have a natural key, so we can delete
|
# don't have a natural key, so we can delete
|
||||||
if natural_key is not None:
|
if natural_key is not None:
|
||||||
|
|||||||
@@ -149,6 +149,8 @@ def get_payload_field_and_value_from_kwargs_or_config_cred(
|
|||||||
|
|
||||||
class CredentialType(HasCreate, base.Base):
|
class CredentialType(HasCreate, base.Base):
|
||||||
|
|
||||||
|
NATURAL_KEY = ('name', 'kind')
|
||||||
|
|
||||||
def silent_delete(self):
|
def silent_delete(self):
|
||||||
if not self.managed_by_tower:
|
if not self.managed_by_tower:
|
||||||
return super(CredentialType, self).silent_delete()
|
return super(CredentialType, self).silent_delete()
|
||||||
@@ -204,6 +206,7 @@ class Credential(HasCopy, HasCreate, base.Base):
|
|||||||
|
|
||||||
dependencies = [CredentialType]
|
dependencies = [CredentialType]
|
||||||
optional_dependencies = [Organization, User, Team]
|
optional_dependencies = [Organization, User, Team]
|
||||||
|
NATURAL_KEY = ('organization', 'name', 'credential_type')
|
||||||
|
|
||||||
def payload(
|
def payload(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ log = logging.getLogger(__name__)
|
|||||||
class Inventory(HasCopy, HasCreate, HasInstanceGroups, HasVariables, base.Base):
|
class Inventory(HasCopy, HasCreate, HasInstanceGroups, HasVariables, base.Base):
|
||||||
|
|
||||||
dependencies = [Organization]
|
dependencies = [Organization]
|
||||||
|
NATURAL_KEY = ('organization', 'name')
|
||||||
|
|
||||||
def print_ini(self):
|
def print_ini(self):
|
||||||
"""Print an ini version of the inventory"""
|
"""Print an ini version of the inventory"""
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class JobTemplate(
|
|||||||
UnifiedJobTemplate):
|
UnifiedJobTemplate):
|
||||||
|
|
||||||
optional_dependencies = [Inventory, Credential, Project]
|
optional_dependencies = [Inventory, Credential, Project]
|
||||||
|
NATURAL_KEY = ('organization', 'name')
|
||||||
|
|
||||||
def launch(self, payload={}):
|
def launch(self, payload={}):
|
||||||
"""Launch the job_template using related->launch endpoint."""
|
"""Launch the job_template using related->launch endpoint."""
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from . import page
|
|||||||
class Label(HasCreate, base.Base):
|
class Label(HasCreate, base.Base):
|
||||||
|
|
||||||
dependencies = [Organization]
|
dependencies = [Organization]
|
||||||
|
NATURAL_KEY = ('organization', 'name')
|
||||||
|
|
||||||
def silent_delete(self):
|
def silent_delete(self):
|
||||||
"""Label pages do not support DELETE requests. Here, we override the base page object
|
"""Label pages do not support DELETE requests. Here, we override the base page object
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ notification_types = (
|
|||||||
class NotificationTemplate(HasCopy, HasCreate, base.Base):
|
class NotificationTemplate(HasCopy, HasCreate, base.Base):
|
||||||
|
|
||||||
dependencies = [Organization]
|
dependencies = [Organization]
|
||||||
|
NATURAL_KEY = ('organization', 'name')
|
||||||
|
|
||||||
def test(self):
|
def test(self):
|
||||||
"""Create test notification"""
|
"""Create test notification"""
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ from . import page
|
|||||||
|
|
||||||
class Organization(HasCreate, HasInstanceGroups, HasNotifications, base.Base):
|
class Organization(HasCreate, HasInstanceGroups, HasNotifications, base.Base):
|
||||||
|
|
||||||
|
NATURAL_KEY = ('name',)
|
||||||
|
|
||||||
def add_admin(self, user):
|
def add_admin(self, user):
|
||||||
if isinstance(user, page.Page):
|
if isinstance(user, page.Page):
|
||||||
user = user.json
|
user = user.json
|
||||||
|
|||||||
@@ -317,6 +317,24 @@ class Page(object):
|
|||||||
page_cls = get_registered_page(endpoint)
|
page_cls = get_registered_page(endpoint)
|
||||||
return page_cls(self.connection, endpoint=endpoint).get(**kw)
|
return page_cls(self.connection, endpoint=endpoint).get(**kw)
|
||||||
|
|
||||||
|
def get_natural_key(self):
|
||||||
|
if not getattr(self, 'NATURAL_KEY', None):
|
||||||
|
raise exc.NoNaturalKey(
|
||||||
|
"Page does not have a natural key: {}".format(getattr(self, 'endpoint', repr(self.__class__)))
|
||||||
|
)
|
||||||
|
natural_key = {}
|
||||||
|
for key in self.NATURAL_KEY:
|
||||||
|
if key in self.related:
|
||||||
|
# FIXME: use caching by url
|
||||||
|
natural_key[key] = self.related[key].get().get_natural_key()
|
||||||
|
elif key in self:
|
||||||
|
natural_key[key] = self[key]
|
||||||
|
if not natural_key:
|
||||||
|
return None
|
||||||
|
|
||||||
|
natural_key['type'] = self['type']
|
||||||
|
return natural_key
|
||||||
|
|
||||||
|
|
||||||
_exception_map = {http.NO_CONTENT: exc.NoContent,
|
_exception_map = {http.NO_CONTENT: exc.NoContent,
|
||||||
http.NOT_FOUND: exc.NotFound,
|
http.NOT_FOUND: exc.NotFound,
|
||||||
@@ -376,6 +394,9 @@ class PageList(object):
|
|||||||
def create(self, *a, **kw):
|
def create(self, *a, **kw):
|
||||||
return self.__item_class__(self.connection).create(*a, **kw)
|
return self.__item_class__(self.connection).create(*a, **kw)
|
||||||
|
|
||||||
|
def get_natural_key(self):
|
||||||
|
raise exc.NoNaturalKey
|
||||||
|
|
||||||
|
|
||||||
class TentativePage(str):
|
class TentativePage(str):
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
|
|||||||
|
|
||||||
optional_dependencies = [Credential, Organization]
|
optional_dependencies = [Credential, Organization]
|
||||||
optional_schedule_fields = tuple()
|
optional_schedule_fields = tuple()
|
||||||
|
NATURAL_KEY = ('organization', 'name')
|
||||||
|
|
||||||
def payload(self, organization, scm_type='git', **kwargs):
|
def payload(self, organization, scm_type='git', **kwargs):
|
||||||
payload = PseudoNamespace(
|
payload = PseudoNamespace(
|
||||||
|
|||||||
@@ -5,7 +5,19 @@ from . import page
|
|||||||
|
|
||||||
class Role(base.Base):
|
class Role(base.Base):
|
||||||
|
|
||||||
pass
|
NATURAL_KEY = ('name',)
|
||||||
|
|
||||||
|
def get_natural_key(self):
|
||||||
|
natural_key = super(Role, self).get_natural_key()
|
||||||
|
related_objs = [
|
||||||
|
related for name, related in self.related.items()
|
||||||
|
if name not in ('users', 'teams')
|
||||||
|
]
|
||||||
|
if related_objs:
|
||||||
|
# FIXME: use caching by url
|
||||||
|
natural_key['content_object'] = related_objs[0].get().get_natural_key()
|
||||||
|
|
||||||
|
return natural_key
|
||||||
|
|
||||||
|
|
||||||
page.register_page(resources.role, Role)
|
page.register_page(resources.role, Role)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from . import base
|
|||||||
|
|
||||||
class Schedule(UnifiedJob):
|
class Schedule(UnifiedJob):
|
||||||
|
|
||||||
pass
|
NATURAL_KEY = ('unified_job_template', 'name')
|
||||||
|
|
||||||
|
|
||||||
page.register_page([resources.schedule,
|
page.register_page([resources.schedule,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from . import page
|
|||||||
class Team(HasCreate, base.Base):
|
class Team(HasCreate, base.Base):
|
||||||
|
|
||||||
dependencies = [Organization]
|
dependencies = [Organization]
|
||||||
|
NATURAL_KEY = ('organization', 'name')
|
||||||
|
|
||||||
def add_user(self, user):
|
def add_user(self, user):
|
||||||
if isinstance(user, page.Page):
|
if isinstance(user, page.Page):
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ from . import page
|
|||||||
|
|
||||||
class User(HasCreate, base.Base):
|
class User(HasCreate, base.Base):
|
||||||
|
|
||||||
|
NATURAL_KEY = ('username',)
|
||||||
|
|
||||||
def payload(self, **kwargs):
|
def payload(self, **kwargs):
|
||||||
payload = PseudoNamespace(
|
payload = PseudoNamespace(
|
||||||
username=kwargs.get('username') or 'User-{}'.format(
|
username=kwargs.get('username') or 'User-{}'.format(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from . import page
|
|||||||
class WorkflowJobTemplateNode(HasCreate, base.Base):
|
class WorkflowJobTemplateNode(HasCreate, base.Base):
|
||||||
|
|
||||||
dependencies = [WorkflowJobTemplate, UnifiedJobTemplate]
|
dependencies = [WorkflowJobTemplate, UnifiedJobTemplate]
|
||||||
|
NATURAL_KEY = ('workflow_job_template', 'identifier')
|
||||||
|
|
||||||
def payload(self, workflow_job_template, unified_job_template, **kwargs):
|
def payload(self, workflow_job_template, unified_job_template, **kwargs):
|
||||||
if not unified_job_template:
|
if not unified_job_template:
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from . import page
|
|||||||
class WorkflowJobTemplate(HasCopy, HasCreate, HasNotifications, HasSurvey, UnifiedJobTemplate):
|
class WorkflowJobTemplate(HasCopy, HasCreate, HasNotifications, HasSurvey, UnifiedJobTemplate):
|
||||||
|
|
||||||
optional_dependencies = [Organization]
|
optional_dependencies = [Organization]
|
||||||
|
NATURAL_KEY = ('organization', 'name')
|
||||||
|
|
||||||
def launch(self, payload={}):
|
def launch(self, payload={}):
|
||||||
"""Launch using related->launch endpoint."""
|
"""Launch using related->launch endpoint."""
|
||||||
|
|||||||
@@ -101,3 +101,8 @@ class UnexpectedAWXState(Common):
|
|||||||
class IsMigrating(Common):
|
class IsMigrating(Common):
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NoNaturalKey(Common):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|||||||
Reference in New Issue
Block a user