diff --git a/pysnc/client.py b/pysnc/client.py index 4d11c13..0ffba93 100644 --- a/pysnc/client.py +++ b/pysnc/client.py @@ -148,7 +148,8 @@ def _set_params(self, record=None): params = {} if record is None else record._parameters() if 'sysparm_display_value' not in params: params['sysparm_display_value'] = 'all' - params['sysparm_exclude_reference_link'] = 'true' # Scratch it! + if 'sysparm_exclude_reference_link' not in params: + params['sysparm_exclude_reference_link'] = 'true' # Scratch it!, by default params['sysparm_suppress_pagination_header'] = 'true' # Required for large queries return params diff --git a/pysnc/record.py b/pysnc/record.py index b5adfbf..3c1600a 100644 --- a/pysnc/record.py +++ b/pysnc/record.py @@ -24,16 +24,19 @@ class GlideElement(str): def __new__(cls, name, value, *args, **kwargs): return super(GlideElement, cls).__new__(cls, value) - def __init__(self, name: str, value=None, display_value=None, parent_record=None): + def __init__(self, name: str, value=None, display_value=None, parent_record=None, link=None): self._name = name self._value = None self._display_value = None self._changed = False + self._link = None if isinstance(value, dict): self._value = value['value'] # only bother to set display value if it's different if self._value != value['display_value']: self._display_value = value['display_value'] + if 'link' in value: + self._link = value['link'] else: self._value = value if display_value: @@ -63,6 +66,12 @@ def get_display_value(self) -> Any: return self._display_value return self._value + def get_link(self) -> Any: + """ + get the link of a field, if it has one, else None + """ + return self._link + def set_value(self, value): """ set the value for the field. Will also set the display_value to `None` @@ -85,6 +94,16 @@ def set_display_value(self, value: Any): self._changed = True self._display_value = value + def set_link(self, link: Any): + """ + set the reference link for the field -- generally speaking does not have any affect upstream (to the server) + """ + if isinstance(link, GlideElement): + link = link.get_link() + if self._link != link: + self._changed = True + self._link = link + def changes(self) -> bool: """ :return: if we have changed this value @@ -103,12 +122,20 @@ def nil(self) -> bool: def serialize(self) -> dict: """ - Returns a dict with the `value`,`display_value` keys - """ - return { - 'value': self.get_value(), - 'display_value': self.get_display_value() - } + Returns a dict with the `value`,`display_value`, `link` keys + """ + return ( + { + 'value': self.get_value(), + 'display_value': self.get_display_value() + } + if self.get_link() is None + else { + 'value': self.get_value(), + 'display_value': self.get_display_value(), + 'link': self.get_link() + } + ) def date_numeric_value(self) -> int: """ @@ -266,6 +293,7 @@ def __init__(self, client: 'ServiceNowClient', table: str, batch_size: int=500, self.__order: str = "ORDERBYsys_id" # we *need* a default order in the event we page, see issue#96 self.__is_new_record: bool = False self.__display_value: Union[bool, str] = 'all' + self.__exclude_reference_link: bool = True self.__rewindable = rewindable def _clear_query(self): @@ -285,6 +313,7 @@ def _parameters(self): ret['sysparm_view'] = self.__view ret['sysparm_display_value'] = str(self.display_value).lower() + ret['sysparm_exclude_reference_link'] = str(self.exclude_reference_link).lower() # Batch size matters! Transaction limits will exceed. # This also means we have to be pretty specific with limits limit = None @@ -423,6 +452,19 @@ def display_value(self, display_value): assert display_value in [True, False, 'all'] self.__display_value = display_value + @property + def exclude_reference_link(self): + return self.__exclude_reference_link + + @exclude_reference_link.setter + def exclude_reference_link(self, exclude_reference_link): + """ + True: Exclude Table API links for reference fields. + False: Include Table API links for reference fields. + """ + assert exclude_reference_link in [True, False] + self.__exclude_reference_link = exclude_reference_link + def order_by(self, column: str): """ Set the order in ascending @@ -697,6 +739,8 @@ def _get_value(self, item, key='value'): o = obj[item] if key == 'display_value': return o.get_display_value() + if key == 'link': + return o.get_link() return o.get_value() return None @@ -764,6 +808,21 @@ def set_display_value(self, field, value): else: c[field].set_display_value(value) + def set_link(self, field, value): + """ + Set the link for a field. + + :param str field: The field + :param value: The Value + """ + c = self._current() + if c is None: + raise NoRecordException('cannot get a value from nothing, did you forget to call next() or initialize()?') + if field not in c: + c[field] = GlideElement(field, link=value, parent_record=self) + else: + c[field].set_link(value) + def get_link(self, no_stack=False) -> str: """ Generate a full URL to the current record. sys_id will be null if there is no current record. @@ -949,7 +1008,7 @@ def add_not_null_query(self, field) -> QueryCondition: """ return self.__query.add_not_null_query(field) - def _serialize(self, record, display_value, fields=None, changes_only=False): + def _serialize(self, record, display_value, fields=None, changes_only=False, exclude_reference_link=True): if isinstance(display_value, str): v_type = 'both' else: @@ -965,19 +1024,27 @@ def compress(obj): if isinstance(value, GlideElement): if changes_only and not value.changes(): continue - if v_type == 'display_value': - ret[key] = value.get_display_value() - elif v_type == 'both': - ret[key] = value.serialize() + if exclude_reference_link or value.get_link() is None: + if v_type == 'display_value': + ret[key] = value.get_display_value() + elif v_type == 'both': + ret[key] = value.serialize() + else: + ret[key] = value.get_value() else: - ret[key] = value.get_value() + serialized = value.serialize() + if v_type == 'display_value': + serialized.pop('value', None) + elif v_type == 'value': + serialized.pop('display_value', None) + ret[key] = serialized else: ret[key] = value.get_value() return ret return compress(record) - def serialize(self, display_value=False, fields=None, fmt=None, changes_only=False) -> Any: + def serialize(self, display_value=False, fields=None, fmt=None, changes_only=False, exclude_reference_link=True) -> Any: """ Turn current record into a dictGlideRecord(None, 'incident') @@ -1005,9 +1072,9 @@ def transform(obj): return transform(self) # i know this is inconsistent, self vs current else: c = self._current() - return self._serialize(c, display_value, fields, changes_only) + return self._serialize(c, display_value, fields, changes_only, exclude_reference_link) - def serialize_all(self, display_value=False, fields=None, fmt=None) -> list: + def serialize_all(self, display_value=False, fields=None, fmt=None, exclude_reference_link=True) -> list: """ Serialize the entire query. See serialize() docs for details on parameters @@ -1016,7 +1083,7 @@ def serialize_all(self, display_value=False, fields=None, fmt=None) -> list: :param fmt: :return: list """ - return [record.serialize(display_value, fields, fmt) for record in self] + return [record.serialize(display_value, fields, fmt, exclude_reference_link) for record in self] def to_pandas(self, columns=None, mode='smart'): """ diff --git a/test/test_snc_element.py b/test/test_snc_element.py index b06630d..b9e1c7d 100644 --- a/test/test_snc_element.py +++ b/test/test_snc_element.py @@ -116,6 +116,23 @@ def test_serialization(self): self.assertEqual(json.dumps(gr2.serialize(display_value=True)), '{"two_field": "Some Value"}') self.assertEqual(gr2.two_field.get_name(), 'two_field') + def test_serialization_with_link(self): + gr = GlideRecord(None, 'incident') + gr.initialize() + gr.test_field = 'some string' + self.assertTrue(isinstance(gr.test_field, GlideElement)) + r = gr.serialize() + self.assertEqual(json.dumps(r), '{"test_field": "some string"}') + + gr.set_value('test_field', 'somevalue') + gr.set_display_value('test_field', 'Some Value') + gr.set_link('test_field', 'https://dev00000.service-now.com/api/now/table/sys___/abcde12345') + print(gr.serialize()) + self.assertEqual(json.dumps(gr.serialize()), '{"test_field": "somevalue"}') + self.assertEqual(json.dumps(gr.serialize(display_value=True, exclude_reference_link=False)), '{"test_field": {"display_value": "Some Value", "link": "https://dev00000.service-now.com/api/now/table/sys___/abcde12345"}}') + self.assertEqual(json.dumps(gr.serialize(display_value=False, exclude_reference_link=False)), '{"test_field": {"value": "somevalue", "link": "https://dev00000.service-now.com/api/now/table/sys___/abcde12345"}}') + self.assertEqual(json.dumps(gr.serialize(display_value='both', exclude_reference_link=False)), '{"test_field": {"value": "somevalue", "display_value": "Some Value", "link": "https://dev00000.service-now.com/api/now/table/sys___/abcde12345"}}') + def test_set_element(self): element = GlideElement('state', '3', 'Pending Change') diff --git a/test/test_snc_serialization.py b/test/test_snc_serialization.py index 0cf54d1..18dcee6 100644 --- a/test/test_snc_serialization.py +++ b/test/test_snc_serialization.py @@ -158,6 +158,37 @@ def test_serialize_display(self): self.assertEqual(data, {'intfield': 5, 'strfield': 'my string display value'}) client.session.close() + def test_serialize_reference_link(self): + client = ServiceNowClient(self.c.server, self.c.credentials) + gr = client.GlideRecord('some_table') + gr.initialize() + gr.reffield = 'my reference' + gr.set_link('reffield', 'https://dev00000.service-now.com/api/now/table/sys___/abcde12345') + + data = gr.serialize(exclude_reference_link=False) + self.assertIsNotNone(data) + self.assertEqual(gr.get_value('reffield'), 'my reference') + self.assertEqual(gr.get_link('reffield'), 'https://dev00000.service-now.com/api/now/table/sys___/abcde12345') + self.assertEqual(gr.serialize(exclude_reference_link=False), {'reffield':{'value': 'my reference','link':'https://dev00000.service-now.com/api/now/table/sys___/abcde12345'}}) + self.assertEqual(data, {'reffield':{'value': 'my reference','link':'https://dev00000.service-now.com/api/now/table/sys___/abcde12345'}}) + client.session.close() + + def test_serialize_reference_link_all(self): + client = ServiceNowClient(self.c.server, self.c.credentials) + gr = client.GlideRecord('some_table') + gr.initialize() + gr.reffield = 'my reference' + gr.set_link('reffield', 'https://dev00000.service-now.com/api/now/table/sys___/abcde12345') + gr.set_display_value('reffield', 'my reference display') + + data = gr.serialize(exclude_reference_link=False) + self.assertIsNotNone(data) + self.assertEqual(gr.get_value('reffield'), 'my reference') + self.assertEqual(gr.get_link('reffield'), 'https://dev00000.service-now.com/api/now/table/sys___/abcde12345') + self.assertEqual(gr.serialize(exclude_reference_link=False), {'reffield':{'value': 'my reference','display_value': 'my reference display', 'link':'https://dev00000.service-now.com/api/now/table/sys___/abcde12345'}}) + self.assertEqual(data, {'reffield':{'value': 'my reference','display_value': 'my reference display', 'link':'https://dev00000.service-now.com/api/now/table/sys___/abcde12345'}}) + client.session.close() + def test_str(self): client = ServiceNowClient(self.c.server, self.c.credentials) gr = client.GlideRecord('some_table')