Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add a link to Glide Element when available #118

Merged
merged 2 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pysnc/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
120 changes: 95 additions & 25 deletions pysnc/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -63,6 +66,12 @@ def get_display_value(self) -> Any:
return self._display_value
return self._value

def get_link(self) -> Any:
vetsin marked this conversation as resolved.
Show resolved Hide resolved
"""
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`
Expand All @@ -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
Expand All @@ -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:
"""
Expand Down Expand Up @@ -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):
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -764,7 +808,22 @@ def set_display_value(self, field, value):
else:
c[field].set_display_value(value)

def get_link(self, no_stack=False) -> str:
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, field=None) -> str:
vetsin marked this conversation as resolved.
Show resolved Hide resolved
"""
Generate a full URL to the current record. sys_id will be null if there is no current record.

Expand All @@ -773,13 +832,16 @@ def get_link(self, no_stack=False) -> str:
:return: The full URL to the current record
:rtype: str
"""
ins = self._client.instance
obj = self._current()
stack = '&sysparm_stack=%s_list.do?sysparm_query=active=true' % self.__table
if no_stack:
stack = ''
id = self.sys_id if obj else 'null'
return "{}/{}.do?sys_id={}{}".format(ins, self.__table, id, stack)
if field is not None:
return self._get_value(field, 'link')
else:
ins = self._client.instance
obj = self._current()
stack = '&sysparm_stack=%s_list.do?sysparm_query=active=true' % self.__table
if no_stack:
stack = ''
id = self.sys_id if obj else 'null'
return "{}/{}.do?sys_id={}{}".format(ins, self.__table, id, stack)

def get_link_list(self) -> Optional[str]:
"""
Expand Down Expand Up @@ -949,7 +1011,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:
Expand All @@ -965,19 +1027,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')

Expand Down Expand Up @@ -1005,9 +1075,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

Expand All @@ -1016,7 +1086,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'):
"""
Expand Down
17 changes: 17 additions & 0 deletions test/test_snc_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down
31 changes: 31 additions & 0 deletions test/test_snc_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Loading