Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

# -*- coding: utf-8 -*- 

# Copyright: (c) 2021, XLAB Steampunk <steampunk@xlab.si> 

# 

# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 

 

from __future__ import absolute_import, division, print_function 

 

__metaclass__ = type 

 

from . import errors 

 

 

def _path(api_path, table, *subpaths): 

return "/".join(api_path + ("table", table) + subpaths) 

 

 

def _query(original=None): 

original = original or dict() 

original.setdefault("sysparm_exclude_reference_link", "true") 

return original 

 

 

class TableClient: 

def __init__(self, client, batch_size=1000): 

# 1000 records is default batch size for ServiceNow REST API, so we also use it 

# as a default. 

self.client = client 

self.batch_size = batch_size 

 

def list_records(self, table, query=None): 

base_query = _query(query) 

base_query["sysparm_limit"] = self.batch_size 

 

offset = 0 

total = 1 # Dummy value that ensures loop executes at least once 

result = [] 

 

while offset < total: 

response = self.client.get( 

_path(self.client.api_path, table), 

query=dict(base_query, sysparm_offset=offset), 

) 

 

result.extend(response.json["result"]) 

total = int(response.headers["x-total-count"]) 

offset += self.batch_size 

 

return result 

 

def get_record(self, table, query, must_exist=False): 

records = self.list_records(table, query) 

 

if len(records) > 1: 

raise errors.ServiceNowError( 

"{0} {1} records match the {2} query.".format( 

len(records), table, query 

) 

) 

 

if must_exist and not records: 

raise errors.ServiceNowError( 

"No {0} records match the {1} query.".format(table, query) 

) 

 

return records[0] if records else None 

 

def get_record_by_sys_id(self, table, sys_id): 

response = self.client.get(_path(self.client.api_path, table, sys_id)) 

record = response.json["result"] 

 

return record 

 

def create_record(self, table, payload, check_mode, query=None): 

if check_mode: 

# Approximate the result using the payload. 

return payload 

 

return self.client.post( 

_path(self.client.api_path, table), payload, query=_query(query) 

).json["result"] 

 

def update_record(self, table, record, payload, check_mode, query=None): 

if check_mode: 

# Approximate the result by manually patching the existing state. 

return dict(record, **payload) 

 

return self.client.patch( 

_path(self.client.api_path, table, record["sys_id"]), 

payload, 

query=_query(query), 

).json["result"] 

 

def delete_record(self, table, record, check_mode): 

if not check_mode: 

self.client.delete(_path(self.client.api_path, table, record["sys_id"])) 

 

 

def find_user(table_client, user_id): 

# TODO: Maybe add a lookup-by-email option too? 

return table_client.get_record("sys_user", dict(user_name=user_id), must_exist=True) 

 

 

def find_assignment_group(table_client, assignment_id): 

return table_client.get_record( 

"sys_user_group", dict(name=assignment_id), must_exist=True 

) 

 

 

def find_standard_change_template(table_client, template_name): 

return table_client.get_record( 

"std_change_producer_version", 

dict(name=template_name), 

must_exist=True, 

) 

 

 

def find_change_request(table_client, change_request_number): 

return table_client.get_record( 

"change_request", dict(number=change_request_number), must_exist=True 

) 

 

 

def find_configuration_item(table_client, item_name): 

return table_client.get_record("cmdb_ci", dict(name=item_name), must_exist=True) 

 

 

def find_problem(table_client, problem_number): 

return table_client.get_record( 

"problem", dict(number=problem_number), must_exist=True 

)