From 9fe7d9eff84618ac6d32dededc7c03aba40e6f17 Mon Sep 17 00:00:00 2001 From: amittell Date: Fri, 28 May 2021 12:54:56 -0400 Subject: [PATCH] 1.0.6 Release (#60) Bugfixes ====== - Resolves Issues #58, #57, #51 and makes auth backwards compatible by defaulting to OAuth if client_id is present without specifying auth. - Order_by is implemented client-side to provide proper sorting as previously documented. --- CHANGELOG.rst | 12 +++ changelogs/.plugin-cache.yaml | 2 +- changelogs/changelog.yaml | 8 ++ changelogs/fragments/58_order_by.yml | 10 +++ plugins/module_utils/service_now.py | 5 +- plugins/modules/snow_record_find.py | 109 +++++++++++++++++---------- 6 files changed, 104 insertions(+), 42 deletions(-) create mode 100644 changelogs/fragments/58_order_by.yml diff --git a/CHANGELOG.rst b/CHANGELOG.rst index eb20bd2..9f37f69 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,18 @@ Servicenow.Servicenow Release Notes .. contents:: Topics + +v1.0.6 +====== + +Bugfixes +-------- + +- Resolves Issues #58, #57, #51 and makes auth backwards compatible by defaulting to OAuth if client_id is present without specifying auth. +- Order_by is implemented client-side to provide proper sorting as previously documented. + +======= + v1.0.5 ====== diff --git a/changelogs/.plugin-cache.yaml b/changelogs/.plugin-cache.yaml index 792683a..518d79f 100644 --- a/changelogs/.plugin-cache.yaml +++ b/changelogs/.plugin-cache.yaml @@ -25,4 +25,4 @@ plugins: shell: {} strategy: {} vars: {} -version: 1.0.5 +version: 1.0.6 \ No newline at end of file diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 85709c6..ec5fd1f 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -40,3 +40,11 @@ releases: fragments: - 27_openid.yml release_date: '2021-04-02' + 1.0.6: + changes: + bugfixes: + - order_by again working by locally sorting return list of records + - makes auth backwards compatible by defaulting to OAuth if client_id is present without specifying auth + fragments: + - 58_order_by.yml + release_date: '2021-05-26' diff --git a/changelogs/fragments/58_order_by.yml b/changelogs/fragments/58_order_by.yml new file mode 100644 index 0000000..173de5e --- /dev/null +++ b/changelogs/fragments/58_order_by.yml @@ -0,0 +1,10 @@ +--- +major_changes: +- auth field now required for anything other than Basic authentication, but, if client_id specified without auth, defaults to OAuth +- refactored client to inherit from AnsibleModule +- supports OpenID Connect authentication protocol +- supports bearer tokens for authentication +minor_changes: +- standardized invocation output +bugfixes: +- order_by again working by locally sorting return list of records \ No newline at end of file diff --git a/plugins/module_utils/service_now.py b/plugins/module_utils/service_now.py index e5e5737..72433d9 100644 --- a/plugins/module_utils/service_now.py +++ b/plugins/module_utils/service_now.py @@ -175,7 +175,10 @@ def _mod_debug(self, key, **kwargs): def _login(self): self.result['changed'] = False if self.params['auth'] == 'basic': - self._auth_basic() + if self.client_id is not None: + self._auth_oauth() + else: + self._auth_basic() elif self.params['auth'] == 'oauth': self._auth_oauth() elif self.params['auth'] == 'token': diff --git a/plugins/modules/snow_record_find.py b/plugins/modules/snow_record_find.py index ea2cae1..5502fda 100644 --- a/plugins/modules/snow_record_find.py +++ b/plugins/modules/snow_record_find.py @@ -218,12 +218,13 @@ try: # This is being managed by ServiceNowModule import pysnow + import re import requests except ImportError: pass -class BuildQuery(object): +class SnowRecordFind(object): ''' This is a BuildQuery manipulation class that constructs a pysnow.QueryBuilder object based on data input. @@ -231,6 +232,31 @@ class BuildQuery(object): def __init__(self, module): self.module = module + + # Define query parameters + self.data = module.params['query'] + self.max_records = self.module.params['max_records'] + self.order_by = self.module.params['order_by'] + self.return_fields = self.module.params['return_fields'] + + # Define sort criteria + self.reverse = False + if self.order_by is not None: + if self.order_by[0] == '-': + self.reverse = True + if self.order_by[0] in ['-', '+']: + self.order_by = self.order_by[1:] + + # Define table parameters + self.table = module.connection.resource( + api_path='/table/' + self.module.params['table']) + self.table.parameters.display_value = self.module.params['display_value'] + self.table.parameters.exclude_reference_link = self.module.params[ + 'exclude_reference_link'] + self.table.parameters.suppress_pagination_header = self.module.params[ + 'suppress_pagination_header'] + + # Define query expression operators self.logic_operators = ["AND", "OR", "NQ"] self.condition_operator = { 'equals': self._condition_closure, @@ -245,17 +271,20 @@ def __init__(self, module): self.accepted_cond_ops = self.condition_operator.keys() self.append_operator = False self.simple_query = True - self.data = module.params['query'] + + # Build the query + self.query = pysnow.QueryBuilder() + self._iterate_operators(self.data) def _condition_closure(self, cond, query_field, query_value): - self.qb.field(query_field) - getattr(self.qb, cond)(query_value) + self.query.field(query_field) + getattr(self.query, cond)(query_value) def _iterate_fields(self, data, logic_op, cond_op): if isinstance(data, dict): for query_field, query_value in data.items(): if self.append_operator: - getattr(self.qb, logic_op)() + getattr(self.query, logic_op)() self.condition_operator[cond_op]( cond_op, query_field, query_value) self.append_operator = True @@ -296,10 +325,39 @@ def _iterate_operators(self, data): ) ) - def build_query(self): - self.qb = pysnow.QueryBuilder() - self._iterate_operators(self.data) - return (self.qb) + def _sort_key(self, e): + if self.order_by in e.keys(): + return self.order_by + else: + prog = re.compile(r'.*' + self.order_by + r'.*') + for key in e.keys(): + if prog.match(key): + return key + return None + + def execute(self): + try: + response = self.table.get( + query=self.query, + limit=self.max_records, + fields=self.return_fields) + except Exception as detail: + self.module.fail( + msg='Failed to find record: {0}'.format(to_native(detail)) + ) + + rlist = response.all() + if len(rlist) > 0: + self.order_by = self._sort_key(rlist[0]) + if self.order_by is not None: + self.module.result['record'] = sorted( + rlist, + key=lambda x: x[self.order_by], + reverse=self.reverse) + else: + self.module.result['record'] = rlist + + self.module.exit() def main(): @@ -347,37 +405,8 @@ def main(): supports_check_mode=True, ) - params = module.params - table = params['table'] - query = params['query'] - max_records = params['max_records'] - display_value = params['display_value'] - exclude_reference_link = params['exclude_reference_link'] - suppress_pagination_header = params['suppress_pagination_header'] - return_fields = params['return_fields'] - - # Do the lookup - try: - bq = BuildQuery(module) - qb = bq.build_query() - table = module.connection.resource(api_path='/table/' + table) - - table.parameters.display_value = display_value - table.parameters.exclude_reference_link = exclude_reference_link - table.parameters.suppress_pagination_header = suppress_pagination_header - - response = table.get( - query=qb, - limit=max_records, - fields=return_fields) - except Exception as detail: - module.fail( - msg='Failed to find record: {0}'.format(to_native(detail)) - ) - - module.result['record'] = response.all() - - module.exit() + query = SnowRecordFind(module) + query.execute() if __name__ == '__main__':