From 781ce6c3eb868d1278320ac7c012f354fa1799c2 Mon Sep 17 00:00:00 2001 From: Lior Blob Date: Wed, 24 Oct 2018 19:29:08 +0300 Subject: [PATCH 01/11] query table, mapping --- Integrations/integration-ServiceNow.yml | 352 +++++++++++++++++++++--- 1 file changed, 312 insertions(+), 40 deletions(-) diff --git a/Integrations/integration-ServiceNow.yml b/Integrations/integration-ServiceNow.yml index a93c2896228b..3ab458d92c74 100644 --- a/Integrations/integration-ServiceNow.yml +++ b/Integrations/integration-ServiceNow.yml @@ -81,14 +81,13 @@ script: del os.environ['http_proxy'] del os.environ['https_proxy'] - ''' GLOBAL VARIABLESS ''' def get_server_url(): url = demisto.params()['url'] url = re.sub('/[\/]+$/', '', url) url = re.sub('\/$', '', url) return url - + ''' GLOBAL VARIABLESS ''' USERNAME = demisto.params()['credentials']['identifier'] PASSWORD = demisto.params()['credentials']['password'] VERIFY_SSL = not demisto.params().get('insecure', False) @@ -102,6 +101,50 @@ script: SERVER_URL = get_server_url() + API + TICKET_STATES = { + 'incident': { + '1': 'New', + '2': 'In Progress', + '3': 'On Hold', + '4': 'Awaiting Caller', + '5': 'Awaiting Evidence', + '6': 'Resolved', + '7': 'Closed', + '8': 'Canceled' + }, + 'problem': { + '1': 'Open', + '2': 'Known Error', + '3': 'Pending Change', + '4': 'Closed/Resolved' + }, + 'change_request': { + '-5': 'New', + '-4': 'Assess', + '-3': 'Authorize', + '-2': 'Scheduled', + '-1': 'Implement', + '0': 'Review', + '3': 'Closed', + '4': 'Canceled' + } + } + + + TICKET_SEVERITY = { + '1': 'High', + '2': 'Medium', + '3': 'Low' + } + + TICKET_PRIORITY = { + '1': 'Critical', + '2': 'High', + '3': 'Moderate', + '4': 'Low', + '5': 'Planning' + } + DEFAULTS = { 'limit':10, 'fetch_limit':10 @@ -152,7 +195,7 @@ script: raise Exception('Error parsing reply - {} - {}'.format(res.content, e.message)) if 'error' in obj: - raise Exception('ServiceNow Error: {}, details: {}'.format(obj['error']['message'], obj['error']['detail'])) + raise Exception('ServiceNow Error: {}, details: {}'.format(obj['error']['message'], obj['error']['detail'], body)) if res.status_code < 200 or res.status_code >= 300: raise Exception('Got status code {} with url {} with body {} with headers {}'.format(str(res.status_code), url, str(res.content), str(res.headers))) @@ -171,6 +214,13 @@ script: def create_ticket_context(data): context = { 'ID': data["sys_id"], + 'State': data.get('state'), + 'Summary': data.get('short_description'), + 'Number': data.get('number'), + 'CreatedOn': data.get('sys_created_on'), + 'AdditionalComments': data.get('comments'), + 'Priority': data.get('priority'), + 'OpenedAt': data.get('opened') } if 'opened_by' in data: @@ -178,11 +228,7 @@ script: if 'assigned_to' in data: context['Assignee'] = data["assigned_to"]['value'] if 'value' in data['assigned_to'] else '' - context['State'] = data.get('state') - context['Summary'] = data.get('short_description') - context['Number'] = data.get('number') - - return context + return createContext(context, removeNull=True) def get_ticket_context(data): if not isinstance(data, list): @@ -193,20 +239,16 @@ script: tickets.append(create_ticket_context(d)) return tickets - def get_ticket_human_readable(tickets): + def get_ticket_human_readable(tickets, ticket_type): if not isinstance(tickets, list): tickets = [tickets] result = [] for ticket in tickets: + hr = { 'Number': ticket['number'], 'System ID': ticket['sys_id'], - 'State': ticket['state'], - 'Impact': ticket.get('impact'), - 'Priority': ticket.get('priority'), - 'Severity': ticket.get('severity'), - 'Urgency': ticket.get('urgency'), 'Created On': ticket.get('sys_created_on'), 'Created By': ticket.get('sys_created_by'), 'Active': ticket.get('active'), @@ -217,8 +259,23 @@ script: 'Resolved by': ticket.get('closed_by'), 'Resolved at': ticket.get('resolved_at'), 'SLA due': ticket.get('sla_due'), - 'Short description': ticket.get('short_description') + 'Short description': ticket.get('short_description'), + 'Additional Comments': ticket.get('comments') } + + if 'impact' in ticket: + hr['Impact'] = TICKET_SEVERITY.get(ticket['impact'], ticket['impact']) + if 'urgency' in ticket: + hr['Urgency'] = TICKET_SEVERITY.get(ticket['urgency'], ticket['urgency']) + if 'severity' in ticket: + hr['Severity'] = TICKET_SEVERITY.get(ticket['severity'], ticket['severity']) + if 'priority' in ticket: + hr['Priority'] = TICKET_PRIORITY.get(ticket['priority'], ticket['priority']) + if 'state' in ticket: + mapped_state = ticket['state'] + if ticket_type in TICKET_STATES: + mapped_state = TICKET_STATES[ticket_type].get(ticket['state'], mapped_state) + hr['State'] = mapped_state result.append(hr) return result @@ -226,6 +283,8 @@ script: def get_body(template_name, custom_fields): body = {} template = template_name + inv_severity = {v: k for k, v in TICKET_SEVERITY.iteritems()} + inv_priority = {v: k for k, v in TICKET_PRIORITY.iteritems()} if template: template = get_template(template_name) @@ -233,7 +292,12 @@ script: for arg in SNOW_ARGS: input_arg = demisto.args().get(arg) if input_arg: - body[arg] = input_arg + if arg in ['impact', 'urgency', 'severity']: + body[arg] = inv_severity.get(input_arg, input_arg) + elif arg == 'priority': + body[arg] = inv_priority.get(input_arg, input_arg) + else: + body[arg] = input_arg elif template_name and arg in template: body[arg] = template[arg] @@ -311,7 +375,7 @@ script: if get_attachments.lower() != 'false': entries = get_ticket_attachment_entries(ticket['sys_id']) - hr = get_ticket_human_readable(ticket) + hr = get_ticket_human_readable(ticket, ticket_type) entry = { 'Type': entryTypes['note'], @@ -376,7 +440,7 @@ script: res = update(ticket_type, custom_fields, template) - hr = get_ticket_human_readable(res['result']) + hr = get_ticket_human_readable(res['result'], ticket_type) entry = { 'Type': entryTypes['note'], @@ -405,7 +469,7 @@ script: res = create(ticket_type, custom_fields, template) - hr = get_ticket_human_readable(res['result']) + hr = get_ticket_human_readable(res['result'], ticket_type) entry = { 'Type': entryTypes['note'], @@ -438,7 +502,7 @@ script: res = add_link(ticket_id, ticket_type, key, link) - hr = get_ticket_human_readable(res['result']) + hr = get_ticket_human_readable(res['result'], ticket_type) entry = { 'Type': entryTypes['note'], 'Contents': res, @@ -465,7 +529,7 @@ script: res = add_comment(ticket_id, ticket_type, key, text) - hr = get_ticket_human_readable(res['result']) + hr = get_ticket_human_readable(res['result'], ticket_type) entry = { 'Type': entryTypes['note'], 'Contents': res, @@ -483,7 +547,7 @@ script: return send_request(path, 'patch', body=body) - def query_command(): + def query_tickets_command(): sysparm_limit = demisto.args().get('limit', DEFAULTS['limit']) sysparm_query = demisto.args().get('query') if not sysparm_query: @@ -493,10 +557,10 @@ script: res = query(ticket_type, sysparm_limit, sysparm_query) - if not res or 'result' not in res: - return 'Cannot find ticket' + if not res or 'result' not in res or len(res['result']) == 0: + return 'No results found' - hr = get_ticket_human_readable(res['result']) + hr = get_ticket_human_readable(res['result'], ticket_type) entry = { 'Type': entryTypes['note'], @@ -512,13 +576,50 @@ script: return entry - def query(ticket_type, sysparm_limit, sysparm_query): + def query_table_command(): + table_name = demisto.args()['table_name'] + sysparm_limit = demisto.args().get('limit', DEFAULTS['limit']) + sysparm_query = demisto.args().get('query') + fields = demisto.args().get('fields') + + res = query(table_name, sysparm_limit, sysparm_query) + + if not res or 'result' not in res or len(res['result']) == 0: + return 'No results found' + + entry = { + 'Type': entryTypes['note'], + 'Contents': res, + 'ContentsFormat': formats['json'] + } + + result = res['result'] + + if fields: + fields = argToList(fields) + result = [dict(filter(lambda kv_pair: kv_pair[0] in fields, r.iteritems())) for r in res['result']] + + if any(result): + entry['ReadableContentsFormat'] = formats['markdown'] + entry['HumanReadable'] = tableToMarkdown('ServiceNow query results for table: ' + table_name, result, removeNull=True) + entry['EntryContext'] = { + 'ServiceNow.Results': createContext(result) + } + else: + entry['ReadableContentsFormat'] = formats['text'] + entry['HumanReadable'] = 'No such fields were found in the result' + + + return entry + + + def query(table_name, sysparm_limit, sysparm_query): query_params = {} query_params['sysparm_limit'] = sysparm_limit if sysparm_query: query_params['sysparm_query'] = sysparm_query - path = 'table/' + ticket_type + path = 'table/' + table_name return send_request(path, 'get', params = query_params) @@ -763,14 +864,16 @@ script: demisto.results(add_link_command()) elif demisto.command() == 'servicenow-add-comment' or demisto.command() == 'servicenow-incident-add-comment': demisto.results(add_comment_command()) - elif demisto.command() == 'servicenow-query' or demisto.command() == 'servicenow-incidents-query': - demisto.results(query_command()) + elif demisto.command() == 'servicenow-query' or demisto.command() == 'servicenow-incidents-query' or demisto.command() == 'servicenow-query-tickets': + demisto.results(query_tickets_command()) elif demisto.command() == 'servicenow-upload-file' or demisto.command() == 'servicenow-incident-upload-file': demisto.results(upload_file_command()) elif demisto.command() == 'servicenow-get-computer': demisto.results(get_computer_command()) elif demisto.command() == 'servicenow-get-groups': demisto.results(get_groups_command()) + if demisto.command() == 'servicenow-query-table': + demisto.results(query_table_command()) except Exception as e: LOG(e) LOG.print_log() @@ -798,39 +901,64 @@ script: description: Whether to retrieve ticket attachments, default false outputs: - contextPath: Ticket.ID - description: ServiceNow ticket System ID + description: The unique ticket identifier. type: string - contextPath: Ticket.Creator - description: ServiceNow ticket creator + description: A string field that indicates the user who created the ticket. type: string + - contextPath: Ticket.CreatedOn + description: The date and time when the ticket was created. + type: date - contextPath: Ticket.Assignee - description: ServiceNow ticket assignee + description: Specifies the user assigned to complete the ticket. By default, + this field uses a reference qualifier to only display users with the itil + role. type: string - contextPath: Ticket.State - description: ServiceNow ticket state + description: Status of the ticket. type: string - contextPath: Ticket.Summary - description: ServiceNow ticket short summary + description: A human-readable title for the record. type: string - contextPath: Ticket.Number - description: ServiceNow ticket number + description: The display value of the ticket. type: string + - contextPath: Ticket.Active + description: Specifies whether work is still being done on a task or whether + the work for the task is complete. + type: boolean + - contextPath: Ticket.AdditionalComments + description: Comments about the task record. + - contextPath: Ticket.Priority + description: Specifies how high a priority the ticket should be for the assignee. + type: string + - contextPath: Ticket.OpenedAt + description: The date and time when the ticket was opened for the first time. + type: date - contextPath: File.Info description: Attachment file info + type: string - contextPath: File.Name description: Attachment file name + type: string - contextPath: File.Size description: Attachment file size + type: number - contextPath: File.SHA1 description: Attachment file SHA1 + type: string - contextPath: File.SHA256 description: Attachment file SHA256 + type: string - contextPath: File.EntryID description: Attachment file entry ID + type: string - contextPath: File.Type description: Attachment file type + type: string - contextPath: File.MD5 description: Attachment file MD5 + type: string description: Retrieve ticket information by specific ticket ID - name: servicenow-incident-get deprecated: true @@ -883,10 +1011,25 @@ script: description: Ticket type defaultValue: incident - name: urgency + auto: PREDEFINED + predefined: + - Low + - Medium + - High description: Ticket urgency - name: severity + auto: PREDEFINED + predefined: + - Low + - Medium + - High description: Ticket severity - name: impact + auto: PREDEFINED + predefined: + - Low + - Medium + - High description: Ticket impact - name: active auto: PREDEFINED @@ -1021,6 +1164,13 @@ script: - name: number description: Ticket number - name: priority + auto: PREDEFINED + predefined: + - Planning + - Low + - Moderate + - High + - Critical description: Priority of the ticket (number) - name: template description: Template name to use as a base to create new tickets. @@ -1035,7 +1185,7 @@ script: description: Type of Change Request ticket defaultValue: normal - name: state - description: State of the ticket (an integer) + description: State of the ticket outputs: - contextPath: Ticket.ID description: ServiceNow ticket System ID @@ -1246,10 +1396,25 @@ script: description: Ticket type defaultValue: incident - name: urgency + auto: PREDEFINED + predefined: + - Low + - Medium + - High description: Ticket urgency - name: severity + auto: PREDEFINED + predefined: + - Low + - Medium + - High description: Ticket severity - name: impact + auto: PREDEFINED + predefined: + - Low + - Medium + - High description: Ticket impact - name: active auto: PREDEFINED @@ -1384,7 +1549,14 @@ script: - name: number description: Ticket number - name: priority - description: Priority of the ticket (number) + auto: PREDEFINED + predefined: + - Planning + - Low + - Moderate + - High + - Critical + description: Priority of the ticket - name: id required: true description: System ID of the ticket to update @@ -1399,7 +1571,7 @@ script: description: Type of Change Request ticket defaultValue: normal - name: state - description: State of the ticket (an integer) + description: State of the ticket description: Update specific ticket by providing ticket id - name: servicenow-incident-update deprecated: true @@ -1650,7 +1822,65 @@ script: description: Publish the link as comment on the ticket, if false will publish the link as WorkNote, format bool description: Add comment to specific ticket by providing ticket id + - name: servicenow-query-tickets + arguments: + - name: limit + description: Limit for how many tickets to retrieve + defaultValue: "10" + - name: ticket_type + auto: PREDEFINED + predefined: + - incident + - problem + - change_request + - sc_request + - sc_task + description: Ticket type + defaultValue: incident + - name: query + description: The query to run. To learn about querying in ServiceNow, see https://docs.servicenow.com/bundle/istanbul-servicenow-platform/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html + outputs: + - contextPath: Ticket.ID + description: The unique ticket identifier. + type: string + - contextPath: Ticket.Creator + description: A string field that indicates the user who created the ticket. + type: string + - contextPath: Ticket.CreatedOn + description: The date and time when the ticket was created. + type: date + - contextPath: Ticket.Assignee + description: Specifies the user assigned to complete the ticket. By default, + this field uses a reference qualifier to only display users with the itil + role. + type: string + - contextPath: Ticket.State + description: Status of the ticket. + type: string + - contextPath: Ticket.Summary + description: A human-readable title for the record. + type: string + - contextPath: Ticket.Number + description: The display value of the ticket. + type: string + - contextPath: Ticket.Active + description: Specifies whether work is still being done on a task or whether + the work for the task is complete. + type: boolean + - contextPath: Ticket.AdditionalComments + description: Comments about the task record. + - contextPath: Ticket.Priority + description: Specifies how high a priority the ticket should be for the assignee. + type: string + - contextPath: Ticket.OpenedAt + description: The date and time when the ticket was opened for the first time. + type: date + - contextPath: Ticket.Escalation + description: Indicates how long the ticket has been open. + type: string + description: Retrieve ticket info with query - name: servicenow-query + deprecated: true arguments: - name: limit description: Limit for how many tickets to retrieve @@ -1689,7 +1919,8 @@ script: - contextPath: Ticket.Number description: ServiceNow ticket number type: string - description: Retrieve ticket info with query + description: Deprecated. Please use servicenow-query-table or servicenow-query-tickets + instead. - name: servicenow-incidents-query deprecated: true arguments: @@ -1824,7 +2055,48 @@ script: description: Comments type: string description: query the cmdb_ci_computer table with a computer code + - name: servicenow-query-table + arguments: + - name: table_name + required: true + description: The name of the table to query + - name: limit + description: Limit for how many tickets to retrieve + defaultValue: "10" + - name: query + description: The query to run. For more information about querying in ServiceNow, + see https://docs.servicenow.com/bundle/istanbul-servicenow-platform/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html + - name: fields + description: Table fields to display and output to the context + isArray: true + outputs: + - contextPath: ServiceNow.Results.sys_id + description: The unique record identifier for the record. + type: string + - contextPath: ServiceNow.Results.sys_updated_by + description: A string field that indicates the user who most recently updated + the record. + type: string + - contextPath: ServiceNow.Results.sys_updated + description: A time-stamp field that indicates the date and time of the most + recent update. + type: date + - contextPath: ServiceNow.Results.sys_created_by + description: A string field that indicates the user who created the record. + type: string + - contextPath: ServiceNow.Results.sys_created_on + description: time-stamp field that indicates when a record was created. + type: date + - contextPath: ServiceNow.Results.sys_mod_count + description: A numeric field that counts the number of updates for this record + since record creation. + type: number + - contextPath: ServiceNow.Results.sys_class_name + description: If the table is extensible, a string field that indicates which + child table contains the record. + type: string + description: Query a specified table in ServiceNow isfetch: true -releaseNotes: "Added catalog task ticket type to dropdown, errors now displayed more gracefully" +releaseNotes: "-" tests: - Run all tests From e8db189077811a74ca8873f2ef02fbf1610275bf Mon Sep 17 00:00:00 2001 From: Lior Blob Date: Sun, 28 Oct 2018 20:07:45 +0200 Subject: [PATCH 02/11] ticket/record, delete-ticket --- Integrations/integration-ServiceNow.yml | 859 +++++++++++++--- TestPlaybooks/playbook-ServiceNow_Test.yml | 1074 +++++++++++++------- 2 files changed, 1473 insertions(+), 460 deletions(-) diff --git a/Integrations/integration-ServiceNow.yml b/Integrations/integration-ServiceNow.yml index 3ab458d92c74..e083866d3909 100644 --- a/Integrations/integration-ServiceNow.yml +++ b/Integrations/integration-ServiceNow.yml @@ -103,46 +103,46 @@ script: TICKET_STATES = { 'incident': { - '1': 'New', - '2': 'In Progress', - '3': 'On Hold', - '4': 'Awaiting Caller', - '5': 'Awaiting Evidence', - '6': 'Resolved', - '7': 'Closed', - '8': 'Canceled' + '1': '1 - New', + '2': '2 - In Progress', + '3': '3 - On Hold', + '4': '4 - Awaiting Caller', + '5': '5 - Awaiting Evidence', + '6': '6 - Resolved', + '7': '7 - Closed', + '8': '8 - Canceled' }, 'problem': { - '1': 'Open', - '2': 'Known Error', - '3': 'Pending Change', - '4': 'Closed/Resolved' + '1': '1 - Open', + '2': '2 - Known Error', + '3': '3 - Pending Change', + '4': '4 - Closed/Resolved' }, 'change_request': { - '-5': 'New', - '-4': 'Assess', - '-3': 'Authorize', - '-2': 'Scheduled', - '-1': 'Implement', - '0': 'Review', - '3': 'Closed', - '4': 'Canceled' + '-5': '-5 - New', + '-4': '-4 - Assess', + '-3': '-3 - Authorize', + '-2': '-2 - Scheduled', + '-1': '-1 - Implement', + '0': '0 - Review', + '3': '3 - Closed', + '4': '4 - Canceled' } } TICKET_SEVERITY = { - '1': 'High', - '2': 'Medium', - '3': 'Low' + '1': '1 - High', + '2': '2 - Medium', + '3': '3 - Low' } TICKET_PRIORITY = { - '1': 'Critical', - '2': 'High', - '3': 'Moderate', - '4': 'Low', - '5': 'Planning' + '1': '1 - Critical', + '2': '2 - High', + '3': '3 - Moderate', + '4': '4 - Low', + '5': '5 - Planning' } DEFAULTS = { @@ -188,10 +188,11 @@ script: res = requests.request(method, url, headers=headers, data=json.dumps(body), params=params, auth=(USERNAME, PASSWORD), verify=VERIFY_SSL) - try: obj = res.json() except Exception as e: + if not res.content: + return '' raise Exception('Error parsing reply - {} - {}'.format(res.content, e.message)) if 'error' in obj: @@ -280,15 +281,8 @@ script: return result - def get_body(template_name, custom_fields): + def get_ticket_fields(template): body = {} - template = template_name - inv_severity = {v: k for k, v in TICKET_SEVERITY.iteritems()} - inv_priority = {v: k for k, v in TICKET_PRIORITY.iteritems()} - - if template: - template = get_template(template_name) - for arg in SNOW_ARGS: input_arg = demisto.args().get(arg) if input_arg: @@ -298,9 +292,20 @@ script: body[arg] = inv_priority.get(input_arg, input_arg) else: body[arg] = input_arg - elif template_name and arg in template: + elif template and arg in template: body[arg] = template[arg] + return body + + def get_body(template, fields, custom_fields): + body = {} + inv_severity = {v: k for k, v in TICKET_SEVERITY.iteritems()} + inv_priority = {v: k for k, v in TICKET_PRIORITY.iteritems()} + + if fields: + for field in fields: + body[field] = fields[field] + if custom_fields: for field in custom_fields: body['u_' + field] = custom_fields[field] @@ -308,14 +313,16 @@ script: return body - def split_custom_fields(fields): + def split_fields(fields): dic_fields = {} - arr_fields = fields.split(';') - for f in arr_fields: - field = f.split('=') - if len(field) > 1: - dic_fields[field[0]] = field[1] + if fields: + arr_fields = fields.split(';') + + for f in arr_fields: + field = f.split('=') + if len(field) > 1: + dic_fields[field[0]] = field[1] return dic_fields @@ -433,12 +440,17 @@ script: return entries - def update_command(): - custom_fields = demisto.args().get('custom_fields') + def update_ticket_command(): + custom_fields = split_fields(demisto.args().get('custom_fields')) template = demisto.args().get('template') ticket_type = get_table_name(demisto.args().get('ticket_type')) + ticket_id = demisto.args()['id'] + + if template: + template = get_template(template_name) + fields = get_ticket_fields(template) - res = update(ticket_type, custom_fields, template) + res = update(ticket_type, ticket_id ,fields, custom_fields, template) hr = get_ticket_human_readable(res['result'], ticket_type) @@ -453,21 +465,22 @@ script: return entry - def update(ticket_type, custom_fields, template): - if custom_fields: - custom_fields = split_custom_fields(custom_fields) - - body = get_body(template, custom_fields) - path = 'table/' + ticket_type + '/' + demisto.args()['id'] + def update(ticket_type, ticket_id, fields, custom_fields, template): + body = get_body(template, fields, custom_fields) + path = 'table/' + ticket_type + '/' + ticket_id return send_request(path, 'patch', body = body) - def create_command(): - custom_fields = demisto.args().get('custom_fields') + def create_ticket_command(): + custom_fields = split_fields(demisto.args().get('custom_fields')) template = demisto.args().get('template') ticket_type = get_table_name(demisto.args().get('ticket_type')) - res = create(ticket_type, custom_fields, template) + if template: + template = get_template(template_name) + fields = get_ticket_fields(template) + + res = create(ticket_type, fields, custom_fields, template) hr = get_ticket_human_readable(res['result'], ticket_type) @@ -478,21 +491,41 @@ script: 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow ticket created\nTicket type: ' + ticket_type, hr, removeNull=True), 'EntryContext': { - 'Ticket(val.ID==obj.ID)': get_ticket_context(res['result']), + 'Ticket(val.ID==obj.ID)': get_ticket_context(res['result']) } } return entry - def create(ticket_type, custom_fields, template): - if custom_fields: - custom_fields = split_custom_fields(custom_fields) - - body = get_body(demisto.args().get('template'), custom_fields) + def create(ticket_type, fields, custom_fields, template): + body = get_body(template, fields, custom_fields) path = 'table/' + ticket_type return send_request(path, 'post', body = body) + def delete_ticket_command(): + ticket_id = demisto.args()['id'] + ticket_type = get_table_name(demisto.args().get('ticket_type')) + + res = delete(ticket_type, ticket_id) + + entry = { + 'Type': entryTypes['note'], + 'Contents': res, + 'ContentsFormat': formats['json'], + 'ReadableContentsFormat': formats['text'], + 'HumanReadable': 'Ticket with ID ' + ticket_id + ' was succesfully deleted.' + } + + return entry + + def delete(ticket_type, ticket_id): + path = 'table/' + ticket_type + '/' + ticket_id + + return send_request(path, 'delete') + + + def add_link_command(): ticket_id = demisto.args()['id'] key = 'comments' if demisto.args().get('post-as-comment', 'false').lower() == 'true' else 'work_notes' @@ -856,10 +889,12 @@ script: fetch_incidents() elif demisto.command() == 'servicenow-get' or demisto.command() == 'servicenow-incident-update': demisto.results(get_ticket_command()) - elif demisto.command() == 'servicenow-update' or demisto.command() == 'servicenow-incident-update': - demisto.results(update_command()) - elif demisto.command() == 'servicenow-create' or demisto.command() == 'servicenow-incident-create': - demisto.results(create_command()) + elif demisto.command() == 'servicenow-update' or demisto.command() == 'servicenow-incident-update' or demisto.command() == 'servicenow-update-ticket': + demisto.results(update_ticket_command()) + elif demisto.command() == 'servicenow-create' or demisto.command() == 'servicenow-incident-create' or demisto.command() == 'servicenow-create-ticket': + demisto.results(create_ticket_command()) + elif demisto.command() == 'servicenow-delete-ticket': + demisto.results(delete_ticket_command()) elif demisto.command() == 'servicenow-add-link' or demisto.command() == 'servicenow-incident-add-link': demisto.results(add_link_command()) elif demisto.command() == 'servicenow-add-comment' or demisto.command() == 'servicenow-incident-add-comment': @@ -877,10 +912,92 @@ script: except Exception as e: LOG(e) LOG.print_log() + raise return_error(e.message) type: python commands: + - name: servicenow-get-ticket + arguments: + - name: id + default: true + description: Ticket System ID to retrieve + - name: ticket_type + auto: PREDEFINED + predefined: + - incident + - problem + - change_request + - sc_request + - sc_task + description: Ticket type + defaultValue: incident + - name: number + description: Ticket number to retrieve + - name: get_attachments + description: Whether to retrieve ticket attachments, default false + outputs: + - contextPath: Ticket.ID + description: The unique ticket identifier. + type: string + - contextPath: Ticket.Creator + description: A string field that indicates the user who created the ticket. + type: string + - contextPath: Ticket.CreatedOn + description: The date and time when the ticket was created. + type: date + - contextPath: Ticket.Assignee + description: Specifies the user assigned to complete the ticket. By default, + this field uses a reference qualifier to only display users with the itil + role. + type: string + - contextPath: Ticket.State + description: Status of the ticket. + type: string + - contextPath: Ticket.Summary + description: A human-readable title for the record. + type: string + - contextPath: Ticket.Number + description: The display value of the ticket. + type: string + - contextPath: Ticket.Active + description: Specifies whether work is still being done on a task or whether + the work for the task is complete. + type: boolean + - contextPath: Ticket.AdditionalComments + description: Comments about the task record. + - contextPath: Ticket.Priority + description: Specifies how high a priority the ticket should be for the assignee. + type: string + - contextPath: Ticket.OpenedAt + description: The date and time when the ticket was opened for the first time. + type: date + - contextPath: File.Info + description: Attachment file info + type: string + - contextPath: File.Name + description: Attachment file name + type: string + - contextPath: File.Size + description: Attachment file size + type: number + - contextPath: File.SHA1 + description: Attachment file SHA1 + type: string + - contextPath: File.SHA256 + description: Attachment file SHA256 + type: string + - contextPath: File.EntryID + description: Attachment file entry ID + type: string + - contextPath: File.Type + description: Attachment file type + type: string + - contextPath: File.MD5 + description: Attachment file MD5 + type: string + description: Retrieve ticket information by specific ticket ID - name: servicenow-get + deprecated: true arguments: - name: id default: true @@ -996,7 +1113,7 @@ script: description: ServiceNow ticket number type: string description: Retrieve ticket information by specific ticket ID - - name: servicenow-create + - name: servicenow-create-ticket arguments: - name: short_description description: Short description of the ticket @@ -1175,7 +1292,7 @@ script: - name: template description: Template name to use as a base to create new tickets. - name: custom_fields - description: 'Custom fields in this format: fieldname=value;fieldname=value...' + description: 'Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;...' - name: type auto: PREDEFINED predefined: @@ -1206,7 +1323,7 @@ script: description: ServiceNow ticket number type: string description: Create new ServiceNow ticket - - name: servicenow-incident-create + - name: servicenow-create deprecated: true arguments: - name: short_description @@ -1217,13 +1334,30 @@ script: - incident - problem - change_request + - sc_request + - sc_task description: Ticket type defaultValue: incident - name: urgency + auto: PREDEFINED + predefined: + - Low + - Medium + - High description: Ticket urgency - name: severity + auto: PREDEFINED + predefined: + - Low + - Medium + - High description: Ticket severity - name: impact + auto: PREDEFINED + predefined: + - Low + - Medium + - High description: Ticket impact - name: active auto: PREDEFINED @@ -1252,7 +1386,7 @@ script: - name: caller_id description: UID Format - name: category - description: Category name + description: Category of ticket - name: caused_by description: UID Format - name: close_code @@ -1358,9 +1492,28 @@ script: - name: number description: Ticket number - name: priority + auto: PREDEFINED + predefined: + - Planning + - Low + - Moderate + - High + - Critical description: Priority of the ticket (number) - name: template description: Template name to use as a base to create new tickets. + - name: custom_fields + description: 'Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;...' + - name: type + auto: PREDEFINED + predefined: + - normal + - standard + - emergency + description: Type of Change Request ticket + defaultValue: normal + - name: state + description: State of the ticket outputs: - contextPath: Ticket.ID description: ServiceNow ticket System ID @@ -1381,7 +1534,8 @@ script: description: ServiceNow ticket number type: string description: Create new ServiceNow ticket - - name: servicenow-update + - name: servicenow-incident-create + deprecated: true arguments: - name: short_description description: Short description of the ticket @@ -1391,39 +1545,22 @@ script: - incident - problem - change_request - - sc_request - - sc_task description: Ticket type defaultValue: incident - name: urgency - auto: PREDEFINED - predefined: - - Low - - Medium - - High description: Ticket urgency - name: severity - auto: PREDEFINED - predefined: - - Low - - Medium - - High description: Ticket severity - name: impact - auto: PREDEFINED - predefined: - - Low - - Medium - - High description: Ticket impact - name: active auto: PREDEFINED predefined: - "true" - "false" - description: Does the ticket active(true/false) + description: Set ticket as Active - name: activity_due - description: 'Format: YYYY-MM-DD HH:MM:SS' + description: Set ticket ActivityDue - format "2016-07-02 21:51:11" glide_date_time - name: additional_assignee_list description: List of assigned users to the ticket - name: approval_history @@ -1543,38 +1680,36 @@ script: - name: work_start description: Date when started to work on the ticket - name: assignment_group - description: UID + description: Set AssignmentGroup - uuid of group like 46b87022a9fe198101a78787e40d7547 - name: incident_state description: integer - name: number description: Ticket number - name: priority - auto: PREDEFINED - predefined: - - Planning - - Low - - Moderate - - High - - Critical - description: Priority of the ticket - - name: id - required: true - description: System ID of the ticket to update - - name: custom_fields - description: 'Custom fields in this format: fieldname=value;fieldname=value...' - - name: type - auto: PREDEFINED - predefined: - - normal - - standard - - emergency - description: Type of Change Request ticket - defaultValue: normal - - name: state - description: State of the ticket - description: Update specific ticket by providing ticket id - - name: servicenow-incident-update - deprecated: true + description: Priority of the ticket (number) + - name: template + description: Template name to use as a base to create new tickets. + outputs: + - contextPath: Ticket.ID + description: ServiceNow ticket System ID + type: string + - contextPath: Ticket.Creator + description: ServiceNow ticket creator + type: string + - contextPath: Ticket.Assignee + description: ServiceNow ticket assignee + type: string + - contextPath: Ticket.State + description: ServiceNow ticket state + type: string + - contextPath: Ticket.Summary + description: ServiceNow ticket short summary + type: string + - contextPath: Ticket.Number + description: ServiceNow ticket number + type: string + description: Create new ServiceNow ticket + - name: servicenow-update-ticket arguments: - name: short_description description: Short description of the ticket @@ -1584,18 +1719,404 @@ script: - incident - problem - change_request + - sc_request + - sc_task description: Ticket type defaultValue: incident - name: urgency + auto: PREDEFINED + predefined: + - Low + - Medium + - High description: Ticket urgency - name: severity + auto: PREDEFINED + predefined: + - Low + - Medium + - High description: Ticket severity - name: impact - description: Ticket impact - - name: active auto: PREDEFINED predefined: - - "true" + - Low + - Medium + - High + description: Ticket impact + - name: active + auto: PREDEFINED + predefined: + - "true" + - "false" + description: Does the ticket active(true/false) + - name: activity_due + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: additional_assignee_list + description: List of assigned users to the ticket + - name: approval_history + description: Ticket history approval + - name: approval_set + description: Set ticket ApprovalSet - format "2016-07-02 21:51:11" glide_date_time + - name: assigned_to + description: To whom the ticket is assigned + - name: business_duration + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: business_service + description: Business service + - name: business_stc + description: Business source + - name: calendar_duration + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: caller_id + description: UID Format + - name: category + description: Category name + - name: caused_by + description: UID Format + - name: close_code + description: Ticket's close code + - name: close_notes + description: Close notes of the ticket + - name: closed_at + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: closed_by + description: User who closed the ticket + - name: cmdb_ci + description: UID Format + - name: comments + description: Format type journal input + - name: comments_and_work_notes + description: Format type journal input + - name: company + description: UID Format + - name: contact_type + description: Contact type + - name: correlation_display + description: Correlation display + - name: correlation_id + description: Correlation id + - name: delivery_plan + description: UID Format + - name: display + auto: PREDEFINED + predefined: + - "true" + - "false" + description: If you want to display comments, work_notes... + - name: description + description: Ticket description + - name: due_date + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: escalation + description: Escalation + - name: expected_start + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: follow_up + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: group_list + description: UID format list + - name: knowledge + auto: PREDEFINED + predefined: + - "true" + - "false" + description: Is the ticket solved in the knowledge base + - name: location + description: Location of the ticket + - name: made_sla + description: SLA of the ticket + - name: notify + auto: PREDEFINED + predefined: + - "1" + - "0" + description: Notify about this ticket + - name: order + description: Order number + - name: parent + description: UID Format + - name: parent_incident + description: UID Format + - name: problem_id + description: UID Format + - name: reassignment_count + description: How many users included in this ticket before + - name: reopen_count + description: How many time the ticket has been reopened + - name: resolved_at + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: resolved_by + description: UID Format + - name: rfc + description: UID + - name: sla_due + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: subcategory + description: Subcategory + - name: sys_updated_by + description: Last updated by + - name: sys_updated_on + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: user_input + description: Input from the end user + - name: watch_list + description: A list of watched tickets + - name: work_end + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: work_notes + description: Format journal list + - name: work_notes_list + description: List with UIDs + - name: work_start + description: Date when started to work on the ticket + - name: assignment_group + description: UID + - name: incident_state + description: integer + - name: number + description: Ticket number + - name: priority + auto: PREDEFINED + predefined: + - Planning + - Low + - Moderate + - High + - Critical + description: Priority of the ticket + - name: id + required: true + description: System ID of the ticket to update + - name: custom_fields + description: 'Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;...' + - name: type + auto: PREDEFINED + predefined: + - normal + - standard + - emergency + description: Type of Change Request ticket + defaultValue: normal + - name: state + description: State of the ticket + description: Update specific ticket by providing ticket id + - name: servicenow-update + deprecated: true + arguments: + - name: short_description + description: Short description of the ticket + - name: ticket_type + auto: PREDEFINED + predefined: + - incident + - problem + - change_request + - sc_request + - sc_task + description: Ticket type + defaultValue: incident + - name: urgency + auto: PREDEFINED + predefined: + - Low + - Medium + - High + description: Ticket urgency + - name: severity + auto: PREDEFINED + predefined: + - Low + - Medium + - High + description: Ticket severity + - name: impact + auto: PREDEFINED + predefined: + - Low + - Medium + - High + description: Ticket impact + - name: active + auto: PREDEFINED + predefined: + - "true" + - "false" + description: Does the ticket active(true/false) + - name: activity_due + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: additional_assignee_list + description: List of assigned users to the ticket + - name: approval_history + description: Ticket history approval + - name: approval_set + description: Set ticket ApprovalSet - format "2016-07-02 21:51:11" glide_date_time + - name: assigned_to + description: To whom the ticket is assigned + - name: business_duration + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: business_service + description: Business service + - name: business_stc + description: Business source + - name: calendar_duration + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: caller_id + description: UID Format + - name: category + description: Category name + - name: caused_by + description: UID Format + - name: close_code + description: Ticket's close code + - name: close_notes + description: Close notes of the ticket + - name: closed_at + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: closed_by + description: User who closed the ticket + - name: cmdb_ci + description: UID Format + - name: comments + description: Format type journal input + - name: comments_and_work_notes + description: Format type journal input + - name: company + description: UID Format + - name: contact_type + description: Contact type + - name: correlation_display + description: Correlation display + - name: correlation_id + description: Correlation id + - name: delivery_plan + description: UID Format + - name: display + auto: PREDEFINED + predefined: + - "true" + - "false" + description: If you want to display comments, work_notes... + - name: description + description: Ticket description + - name: due_date + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: escalation + description: Escalation + - name: expected_start + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: follow_up + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: group_list + description: UID format list + - name: knowledge + auto: PREDEFINED + predefined: + - "true" + - "false" + description: Is the ticket solved in the knowledge base + - name: location + description: Location of the ticket + - name: made_sla + description: SLA of the ticket + - name: notify + auto: PREDEFINED + predefined: + - "1" + - "0" + description: Notify about this ticket + - name: order + description: Order number + - name: parent + description: UID Format + - name: parent_incident + description: UID Format + - name: problem_id + description: UID Format + - name: reassignment_count + description: How many users included in this ticket before + - name: reopen_count + description: How many time the ticket has been reopened + - name: resolved_at + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: resolved_by + description: UID Format + - name: rfc + description: UID + - name: sla_due + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: subcategory + description: Subcategory + - name: sys_updated_by + description: Last updated by + - name: sys_updated_on + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: user_input + description: Input from the end user + - name: watch_list + description: A list of watched tickets + - name: work_end + description: 'Format: YYYY-MM-DD HH:MM:SS' + - name: work_notes + description: Format journal list + - name: work_notes_list + description: List with UIDs + - name: work_start + description: Date when started to work on the ticket + - name: assignment_group + description: UID + - name: incident_state + description: integer + - name: number + description: Ticket number + - name: priority + auto: PREDEFINED + predefined: + - Planning + - Low + - Moderate + - High + - Critical + description: Priority of the ticket + - name: id + required: true + description: System ID of the ticket to update + - name: custom_fields + description: 'Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;...' + - name: type + auto: PREDEFINED + predefined: + - normal + - standard + - emergency + description: Type of Change Request ticket + defaultValue: normal + - name: state + description: State of the ticket + description: Update specific ticket by providing ticket id + - name: servicenow-incident-update + deprecated: true + arguments: + - name: short_description + description: Short description of the ticket + - name: ticket_type + auto: PREDEFINED + predefined: + - incident + - problem + - change_request + description: Ticket type + defaultValue: incident + - name: urgency + description: Ticket urgency + - name: severity + description: Ticket severity + - name: impact + description: Ticket impact + - name: active + auto: PREDEFINED + predefined: + - "true" - "false" description: Does the ticket active(true/false) - name: activity_due @@ -1730,6 +2251,21 @@ script: required: true description: System ID of the ticket to update description: Update specific ticket by providing ticket id + - name: servicenow-delete-ticket + arguments: + - name: id + required: true + description: Ticket System ID + - name: ticket_type + auto: PREDEFINED + predefined: + - incident + - problem + - change_request + - sc_request + - sc_task + description: Ticket type + description: Delete a ticket from ServiceNow - name: servicenow-add-link arguments: - name: id @@ -2096,6 +2632,89 @@ script: child table contains the record. type: string description: Query a specified table in ServiceNow + - name: servicenow-create-record + arguments: + - name: table_name + required: true + description: The name of the table to create a record in + - name: fields + description: 'Fields and their values to create the record with, in the format: + fieldname1=value;fieldname2=value;...' + - name: custom_fields + description: 'Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;...' + outputs: + - contextPath: ServiceNow.Record.sys_id + description: The unique record identifier for the record. + type: string + - contextPath: ServiceNow.Record.sys_updated_by + description: A string field that indicates the user who most recently updated + the record. + type: string + - contextPath: ServiceNow.Record.sys_updated + description: A time-stamp field that indicates the date and time of the most + recent update. + type: date + - contextPath: ServiceNow.Record.sys_created_by + description: A string field that indicates the user who created the record. + type: string + - contextPath: ServiceNow.Record.sys_created_on + description: time-stamp field that indicates when a record was created. + type: date + - contextPath: ServiceNow.Record.sys_mod_count + description: A numeric field that counts the number of updates for this record + since record creation. + type: number + - contextPath: ServiceNow.Record.sys_class_name + description: If the table is extensible, a string field that indicates which + child table contains the record. + type: string + description: Create a new record in a specified ServiceNow table + - name: servicenow-update-record + arguments: + - name: table_name + required: true + description: The name of the table to update the record in + - name: id + required: true + description: The system ID of the ticket to update + - name: fields + description: 'Fields and their values to update in the record, in the format: + fieldname1=value;fieldname2=value;...' + outputs: + - contextPath: ServiceNow.Record.sys_id + description: The unique record identifier for the record. + type: string + - contextPath: ServiceNow.Record.sys_updated_by + description: A string field that indicates the user who most recently updated + the record. + type: string + - contextPath: ServiceNow.Record.sys_updated + description: A time-stamp field that indicates the date and time of the most + recent update. + type: date + - contextPath: ServiceNow.Record.sys_created_by + description: A string field that indicates the user who created the record. + type: string + - contextPath: ServiceNow.Record.sys_created_on + description: time-stamp field that indicates when a record was created. + type: date + - contextPath: ServiceNow.Record.sys_mod_count + description: A numeric field that counts the number of updates for this record + since record creation. + type: number + - contextPath: ServiceNow.Record.sys_class_name + description: If the table is extensible, a string field that indicates which + child table contains the record. + type: string + description: Update a record in a specified ServiceNow table + - name: servicenow-delete-record + arguments: + - name: table_name + required: true + - name: id + required: true + description: The system ID of the ticket to update + description: Delete a record in a specified ServiceNow table isfetch: true releaseNotes: "-" tests: diff --git a/TestPlaybooks/playbook-ServiceNow_Test.yml b/TestPlaybooks/playbook-ServiceNow_Test.yml index 22683cfd9989..0d223c602ef1 100644 --- a/TestPlaybooks/playbook-ServiceNow_Test.yml +++ b/TestPlaybooks/playbook-ServiceNow_Test.yml @@ -1,15 +1,15 @@ -id: cafc6f55-b05a-4349-8c21-dd05fb7db2b4 -version: 81 +id: servicenow_test +version: 20 name: ServiceNow Test -description: This playbook tests the SEPM integration commands. +description: This playbook tests the ServiceNow integration commands. starttaskid: "0" tasks: "0": id: "0" - taskid: 381f9fbd-5059-450f-8e01-50588a575aca + taskid: 67dc347d-93a3-4375-8eed-b618cb1f3f86 type: start task: - id: 381f9fbd-5059-450f-8e01-50588a575aca + id: 67dc347d-93a3-4375-8eed-b618cb1f3f86 version: -1 name: "" iscommand: false @@ -17,6 +17,7 @@ tasks: nexttasks: '#none#': - "1" + separatecontext: false view: |- { "position": { @@ -24,12 +25,13 @@ tasks: "y": 50 } } + note: false "1": id: "1" - taskid: 92ffe447-ad97-46b5-8b38-078abbd3b038 + taskid: 11063b19-9c0f-4515-8059-b4b497c968b2 type: regular task: - id: 92ffe447-ad97-46b5-8b38-078abbd3b038 + id: 11063b19-9c0f-4515-8059-b4b497c968b2 version: -1 name: DeleteContext (all) description: Delete field from context @@ -41,8 +43,10 @@ tasks: '#none#': - "15" scriptarguments: - all: "yes" - key: "" + all: + simple: "yes" + key: {} + separatecontext: false view: |- { "position": { @@ -50,12 +54,13 @@ tasks: "y": 195 } } + note: false "2": id: "2" - taskid: 3412f648-06c7-48e7-8262-6bbc72c5eccd + taskid: af8bf494-e593-4f57-86fe-93de46f91ad8 type: regular task: - id: 3412f648-06c7-48e7-8262-6bbc72c5eccd + id: af8bf494-e593-4f57-86fe-93de46f91ad8 version: -1 name: 'servicenow-create, Summary: "Hello World"' description: Create new ServiceNow ticket @@ -68,71 +73,77 @@ tasks: - "6" - "35" scriptarguments: - active: "" - activity_due: "" - additional_assignee_list: "" - approval: "" - approval_history: "" - approval_set: "" - assigned_to: Johnny Doe - assignment_group: "" - business_duration: "" - business_service: "" - business_stc: "" - calendar_duration: "" - caller_id: "" - category: "" - caused_by: "" - close_code: "" - close_notes: "" - closed_at: "" - closed_by: "" - cmdb_ci: "" - comments: "" - comments_and_work_notes: "" - company: "" - contact_type: "" - correlation_display: "" - correlation_id: "" - delivery_plan: "" - description: "" - display: "" - due_date: "" - escalation: "" - expected_start: "" - follow_up: "" - group_list: "" - impact: "" - incident_state: "" - knowledge: "" - location: "" - made_sla: "" - notify: "" - number: "" - order: "" - parent: "" - parent_incident: "" - priority: "" - problem_id: "" - reassignment_count: "" - reopen_count: "" - resolved_at: "" - resolved_by: "" - rfc: "" - severity: "" - short_description: Hello World - sla_due: "" - subcategory: "" - sys_updated_by: "" - sys_updated_on: "" - ticket_type: "" - urgency: "" - user_input: "" - watch_list: "" - work_end: "" - work_notes: "" - work_notes_list: "" - work_start: "" + active: {} + activity_due: {} + additional_assignee_list: {} + approval_history: {} + approval_set: {} + assigned_to: + simple: Johnny Doe + assignment_group: {} + business_duration: {} + business_service: {} + business_stc: {} + calendar_duration: {} + caller_id: {} + category: {} + caused_by: {} + close_code: {} + close_notes: {} + closed_at: {} + closed_by: {} + cmdb_ci: {} + comments: {} + comments_and_work_notes: {} + company: {} + contact_type: {} + correlation_display: {} + correlation_id: {} + custom_fields: {} + delivery_plan: {} + description: {} + display: {} + due_date: {} + escalation: {} + expected_start: {} + follow_up: {} + group_list: {} + impact: {} + incident_state: {} + knowledge: {} + location: {} + made_sla: {} + notify: {} + number: {} + order: {} + parent: {} + parent_incident: {} + priority: {} + problem_id: {} + reassignment_count: {} + reopen_count: {} + resolved_at: {} + resolved_by: {} + rfc: {} + severity: {} + short_description: + simple: Hello World + sla_due: {} + state: {} + subcategory: {} + sys_updated_by: {} + sys_updated_on: {} + template: {} + ticket_type: {} + type: {} + urgency: {} + user_input: {} + watch_list: {} + work_end: {} + work_notes: {} + work_notes_list: {} + work_start: {} + separatecontext: false view: |- { "position": { @@ -140,12 +151,13 @@ tasks: "y": 515 } } + note: false "6": id: "6" - taskid: 7c99b12e-6d0c-419c-80d1-5724a3434c67 + taskid: b5e47340-57f7-4197-803e-da438c182caa type: regular task: - id: 7c99b12e-6d0c-419c-80d1-5724a3434c67 + id: b5e47340-57f7-4197-803e-da438c182caa version: -1 name: 'VerifyContextFields: Summary, Assignee' scriptName: VerifyContextFields @@ -156,16 +168,21 @@ tasks: '#none#': - "7" scriptarguments: - field1: Ticket.Assignee - field2: Ticket.Summary - field3: "" - field4: "" - field5: "" - value1: Johnny Doe - value2: Hello World - value3: "" - value4: "" - value5: "" + field1: + simple: Ticket.Assignee + field2: + simple: Ticket.Summary + field3: {} + field4: {} + field5: {} + value1: + simple: Johnny Doe + value2: + simple: Hello World + value3: {} + value4: {} + value5: {} + separatecontext: false view: |- { "position": { @@ -173,12 +190,13 @@ tasks: "y": 690 } } + note: false "7": id: "7" - taskid: b693a8e6-bec7-4cfd-8c35-45441c94c137 + taskid: 2e397644-2c39-4620-8b20-6021af54838f type: regular task: - id: b693a8e6-bec7-4cfd-8c35-45441c94c137 + id: 2e397644-2c39-4620-8b20-6021af54838f version: -1 name: 'servicenow-create, Summary: "Lovely Day"' description: Create new ServiceNow ticket @@ -191,71 +209,77 @@ tasks: - "8" - "36" scriptarguments: - active: "" - activity_due: "" - additional_assignee_list: "" - approval: "" - approval_history: "" - approval_set: "" - assigned_to: Lucy Liu - assignment_group: "" - business_duration: "" - business_service: "" - business_stc: "" - calendar_duration: "" - caller_id: "" - category: "" - caused_by: "" - close_code: "" - close_notes: "" - closed_at: "" - closed_by: "" - cmdb_ci: "" - comments: "" - comments_and_work_notes: "" - company: "" - contact_type: "" - correlation_display: "" - correlation_id: "" - delivery_plan: "" - description: "" - display: "" - due_date: "" - escalation: "" - expected_start: "" - follow_up: "" - group_list: "" - impact: "" - incident_state: "" - knowledge: "" - location: "" - made_sla: "" - notify: "" - number: "" - order: "" - parent: "" - parent_incident: "" - priority: "" - problem_id: "" - reassignment_count: "" - reopen_count: "" - resolved_at: "" - resolved_by: "" - rfc: "" - severity: "" - short_description: Lovely Day - sla_due: "" - subcategory: "" - sys_updated_by: "" - sys_updated_on: "" - ticket_type: "" - urgency: "" - user_input: "" - watch_list: "" - work_end: "" - work_notes: "" - work_notes_list: "" - work_start: "" + active: {} + activity_due: {} + additional_assignee_list: {} + approval_history: {} + approval_set: {} + assigned_to: + simple: Lucy Liu + assignment_group: {} + business_duration: {} + business_service: {} + business_stc: {} + calendar_duration: {} + caller_id: {} + category: {} + caused_by: {} + close_code: {} + close_notes: {} + closed_at: {} + closed_by: {} + cmdb_ci: {} + comments: {} + comments_and_work_notes: {} + company: {} + contact_type: {} + correlation_display: {} + correlation_id: {} + custom_fields: {} + delivery_plan: {} + description: {} + display: {} + due_date: {} + escalation: {} + expected_start: {} + follow_up: {} + group_list: {} + impact: {} + incident_state: {} + knowledge: {} + location: {} + made_sla: {} + notify: {} + number: {} + order: {} + parent: {} + parent_incident: {} + priority: {} + problem_id: {} + reassignment_count: {} + reopen_count: {} + resolved_at: {} + resolved_by: {} + rfc: {} + severity: {} + short_description: + simple: Lovely Day + sla_due: {} + state: {} + subcategory: {} + sys_updated_by: {} + sys_updated_on: {} + template: {} + ticket_type: {} + type: {} + urgency: {} + user_input: {} + watch_list: {} + work_end: {} + work_notes: {} + work_notes_list: {} + work_start: {} + separatecontext: false view: |- { "position": { @@ -263,12 +287,13 @@ tasks: "y": 865 } } + note: false "8": id: "8" - taskid: db57dc71-c470-41b7-8bfd-069175cb8365 + taskid: a826bc45-36b0-402d-8dea-a369177ee1d3 type: regular task: - id: db57dc71-c470-41b7-8bfd-069175cb8365 + id: a826bc45-36b0-402d-8dea-a369177ee1d3 version: -1 name: 'VerifyContextFields: Ticket[1].Summary, Ticket[1].Assignee' scriptName: VerifyContextFields @@ -279,16 +304,21 @@ tasks: '#none#': - "18" scriptarguments: - field1: Ticket.[1].Assignee - field2: Ticket.[1].Summary - field3: "" - field4: "" - field5: "" - value1: Lucy Liu - value2: Lovely Day - value3: "" - value4: "" - value5: "" + field1: + simple: Ticket.[1].Assignee + field2: + simple: Ticket.[1].Summary + field3: {} + field4: {} + field5: {} + value1: + simple: Lucy Liu + value2: + simple: Lovely Day + value3: {} + value4: {} + value5: {} + separatecontext: false view: |- { "position": { @@ -296,12 +326,13 @@ tasks: "y": 1040 } } + note: false "15": id: "15" - taskid: 39d917e4-fee6-453e-8def-e5e6baeb7391 + taskid: d2b17f13-7fbb-4591-8dcf-0722dbd5c8c1 type: title task: - id: 39d917e4-fee6-453e-8def-e5e6baeb7391 + id: d2b17f13-7fbb-4591-8dcf-0722dbd5c8c1 version: -1 name: Create two tickets type: title @@ -310,6 +341,7 @@ tasks: nexttasks: '#none#': - "2" + separatecontext: false view: |- { "position": { @@ -317,12 +349,13 @@ tasks: "y": 370 } } + note: false "18": id: "18" - taskid: 66e594c8-ded4-4cda-810c-1e05d231b633 + taskid: 1f1dcd0b-d837-475e-8016-01e116314957 type: title task: - id: 66e594c8-ded4-4cda-810c-1e05d231b633 + id: 1f1dcd0b-d837-475e-8016-01e116314957 version: -1 name: Query and update tickets type: title @@ -331,6 +364,7 @@ tasks: nexttasks: '#none#': - "24" + separatecontext: false view: |- { "position": { @@ -338,12 +372,13 @@ tasks: "y": 1215 } } + note: false "24": id: "24" - taskid: dfcd1d68-fc93-458f-8164-e8f8e67686a2 + taskid: 41382c39-cb5e-43c7-8748-d509be51c63d type: regular task: - id: dfcd1d68-fc93-458f-8164-e8f8e67686a2 + id: 41382c39-cb5e-43c7-8748-d509be51c63d version: -1 name: 'servicenow-query Ticket #0' script: ServiceNow|||servicenow-query @@ -354,9 +389,11 @@ tasks: '#none#': - "37" scriptarguments: - limit: "" - sysparm_query: sys_id=${Ticket.[0].ID} - ticket_type: "" + limit: {} + sysparm_query: + simple: sys_id=${Ticket.[0].ID} + ticket_type: {} + separatecontext: false view: |- { "position": { @@ -364,12 +401,13 @@ tasks: "y": 1360 } } + note: false "25": id: "25" - taskid: 853cd34d-5b1c-4302-87d6-8e4fe36a5e03 + taskid: 1968f536-0a02-4fdc-8880-518d720ee139 type: regular task: - id: 853cd34d-5b1c-4302-87d6-8e4fe36a5e03 + id: 1968f536-0a02-4fdc-8880-518d720ee139 version: -1 name: 'servicenow-get Ticket #1' script: ServiceNow|||servicenow-get @@ -380,9 +418,11 @@ tasks: '#none#': - "41" scriptarguments: - id: ${Ticket.[1].ID} - number: "" - ticket_type: "" + id: + simple: ${Ticket.[1].ID} + number: {} + ticket_type: {} + separatecontext: false view: |- { "position": { @@ -390,12 +430,13 @@ tasks: "y": 1710 } } + note: false "26": id: "26" - taskid: c84f7c89-68f1-44e4-8fa2-474e76662296 + taskid: c9c7c8fa-e24c-4772-83f1-54e09105c562 type: regular task: - id: c84f7c89-68f1-44e4-8fa2-474e76662296 + id: c9c7c8fa-e24c-4772-83f1-54e09105c562 version: -1 name: 'servicenow-update Ticket #0, Summary: "Goodbye Please", Assignee: "New Assignee"' @@ -407,72 +448,78 @@ tasks: '#none#': - "28" scriptarguments: - active: "" - activity_due: "" - additional_assignee_list: "" - approval: "" - approval_history: "" - approval_set: "" - assigned_to: New Assignee - assignment_group: "" - business_duration: "" - business_service: "" - business_stc: "" - calendar_duration: "" - caller_id: "" - category: "" - caused_by: "" - close_code: "" - close_notes: "" - closed_at: "" - closed_by: "" - cmdb_ci: "" - comments: "" - comments_and_work_notes: "" - company: "" - contact_type: "" - correlation_display: "" - correlation_id: "" - delivery_plan: "" - description: "" - display: "" - due_date: "" - escalation: "" - expected_start: "" - follow_up: "" - group_list: "" - id: ${Ticket.[0].ID} - impact: "" - incident_state: "" - knowledge: "" - location: "" - made_sla: "" - notify: "" - number: "" - order: "" - parent: "" - parent_incident: "" - priority: "" - problem_id: "" - reassignment_count: "" - reopen_count: "" - resolved_at: "" - resolved_by: "" - rfc: "" - severity: "" - short_description: Goodbye Please - sla_due: "" - subcategory: "" - sys_updated_by: "" - sys_updated_on: "" - ticket_type: "" - urgency: "" - user_input: "" - watch_list: "" - work_end: "" - work_notes: "" - work_notes_list: "" - work_start: "" + active: {} + activity_due: {} + additional_assignee_list: {} + approval_history: {} + approval_set: {} + assigned_to: + simple: New Assignee + assignment_group: {} + business_duration: {} + business_service: {} + business_stc: {} + calendar_duration: {} + caller_id: {} + category: {} + caused_by: {} + close_code: {} + close_notes: {} + closed_at: {} + closed_by: {} + cmdb_ci: {} + comments: {} + comments_and_work_notes: {} + company: {} + contact_type: {} + correlation_display: {} + correlation_id: {} + custom_fields: {} + delivery_plan: {} + description: {} + display: {} + due_date: {} + escalation: {} + expected_start: {} + follow_up: {} + group_list: {} + id: + simple: ${Ticket.[0].ID} + impact: {} + incident_state: {} + knowledge: {} + location: {} + made_sla: {} + notify: {} + number: {} + order: {} + parent: {} + parent_incident: {} + priority: {} + problem_id: {} + reassignment_count: {} + reopen_count: {} + resolved_at: {} + resolved_by: {} + rfc: {} + severity: {} + short_description: + simple: Goodbye Please + sla_due: {} + state: {} + subcategory: {} + sys_updated_by: {} + sys_updated_on: {} + ticket_type: {} + type: {} + urgency: {} + user_input: {} + watch_list: {} + work_end: {} + work_notes: {} + work_notes_list: {} + work_start: {} + separatecontext: false view: |- { "position": { @@ -480,26 +527,29 @@ tasks: "y": 2060 } } + note: false "28": id: "28" - taskid: 650a81e1-1726-45ad-8201-d655ea651a21 + taskid: 990af179-91e5-438d-8976-1b126da46be0 type: regular task: - id: 650a81e1-1726-45ad-8201-d655ea651a21 + id: 990af179-91e5-438d-8976-1b126da46be0 version: -1 name: 'servicenow-query Ticket #0' - script: ServiceNow|||servicenow-query + script: '|||servicenow-query-tickets' type: regular iscommand: true - brand: ServiceNow + brand: "" nexttasks: '#none#': - "40" - "29" scriptarguments: - limit: "" - sysparm_query: sys_id=${Ticket.[0].ID} - ticket_type: "" + limit: {} + query: + simple: sys_id=${Ticket.[0].ID} + ticket_type: {} + separatecontext: false view: |- { "position": { @@ -507,14 +557,15 @@ tasks: "y": 2235 } } + note: false "29": id: "29" - taskid: 8a20b448-27f1-4d57-894f-081706a391b2 + taskid: 13f3c178-59be-42a5-8cac-a3f943e074d1 type: regular task: - id: 8a20b448-27f1-4d57-894f-081706a391b2 + id: 13f3c178-59be-42a5-8cac-a3f943e074d1 version: -1 - name: 'VerifyContextFields (New entry created - #2, Ticket[2])' + name: 'VerifyContextFields (Entry updated - #0, Ticket[0])' scriptName: VerifyContextFields type: regular iscommand: false @@ -523,16 +574,21 @@ tasks: '#none#': - "30" scriptarguments: - field1: Ticket.[2].Summary - field2: Ticket.[2].Assignee - field3: "" - field4: "" - field5: "" - value1: Goodbye Please - value2: New Assignee - value3: "" - value4: "" - value5: "" + field1: + simple: Ticket.[0].Summary + field2: + simple: Ticket.[0].Assignee + field3: {} + field4: {} + field5: {} + value1: + simple: Goodbye Please + value2: + simple: New Assignee + value3: {} + value4: {} + value5: {} + separatecontext: false view: |- { "position": { @@ -540,12 +596,13 @@ tasks: "y": 2410 } } + note: false "30": id: "30" - taskid: e0bc90c2-1906-4a6e-8efc-6fbc000c621b + taskid: 581c4f00-74a8-4f5f-8760-d377605b6221 type: regular task: - id: e0bc90c2-1906-4a6e-8efc-6fbc000c621b + id: 581c4f00-74a8-4f5f-8760-d377605b6221 version: -1 name: 'servicenow-add-comment Ticket #0' script: ServiceNow|||servicenow-add-comment @@ -556,10 +613,13 @@ tasks: '#none#': - "42" scriptarguments: - comment: This is a new comment - id: ${Ticket.[0].ID} - post-as-comment: "" - ticket_type: "" + comment: + simple: This is a new comment + id: + simple: ${Ticket.[0].ID} + post-as-comment: {} + ticket_type: {} + separatecontext: false view: |- { "position": { @@ -567,14 +627,15 @@ tasks: "y": 2585 } } + note: false "31": id: "31" - taskid: e195f537-64fe-4ef0-8e97-ea3b9115c055 + taskid: 9c3d92e3-6ac0-4fde-8844-d8aeaf41af7d type: regular task: - id: e195f537-64fe-4ef0-8e97-ea3b9115c055 + id: 9c3d92e3-6ac0-4fde-8844-d8aeaf41af7d version: -1 - name: 'servicenow-add-link Ticket #2' + name: 'servicenow-add-link Ticket #0' script: ServiceNow|||servicenow-add-link type: regular iscommand: true @@ -583,11 +644,14 @@ tasks: '#none#': - "43" scriptarguments: - id: ${Ticket.[2].ID} - link: http://www.google.com - post-as-comment: "" - text: "" - ticket_type: "" + id: + simple: ${Ticket.[0].ID} + link: + simple: http://www.google.com + post-as-comment: {} + text: {} + ticket_type: {} + separatecontext: false view: |- { "position": { @@ -595,12 +659,13 @@ tasks: "y": 2935 } } + note: false "33": id: "33" - taskid: 7fa410cb-e5ef-40c5-8e94-4ef635791f4d + taskid: 54c7fb27-4836-4f5e-8c7c-7adb523b031a type: regular task: - id: 7fa410cb-e5ef-40c5-8e94-4ef635791f4d + id: 54c7fb27-4836-4f5e-8c7c-7adb523b031a version: -1 name: Upload File to War Room scriptName: FileCreateAndUpload @@ -611,8 +676,12 @@ tasks: '#none#': - "46" scriptarguments: - data: file data - filename: test_file.txt + data: + simple: file data + entryId: {} + filename: + simple: test_file.txt + separatecontext: false view: |- { "position": { @@ -620,12 +689,13 @@ tasks: "y": 3285 } } + note: false "35": id: "35" - taskid: 24a58896-9b8e-4a1a-8075-51a6f5acf377 + taskid: 8c055dee-25f1-4f73-8fb3-bf3fa04d7078 type: regular task: - id: 24a58896-9b8e-4a1a-8075-51a6f5acf377 + id: 8c055dee-25f1-4f73-8fb3-bf3fa04d7078 version: -1 name: 'Verify output contains: "Hello World"' scriptName: VerifyHumanReadableContains @@ -636,8 +706,11 @@ tasks: '#none#': - "7" scriptarguments: - humanReadableEntryId: ${lastCompletedTaskEntries.[0]} - substring: Hello World + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Hello World + separatecontext: false view: |- { "position": { @@ -645,12 +718,13 @@ tasks: "y": 690 } } + note: false "36": id: "36" - taskid: c2114790-868f-48bd-8560-ba0d916f13af + taskid: 795072c5-5b27-4d2a-83af-640e4d8147d0 type: regular task: - id: c2114790-868f-48bd-8560-ba0d916f13af + id: 795072c5-5b27-4d2a-83af-640e4d8147d0 version: -1 name: 'Verify output contains: "Lovely Day"' scriptName: VerifyHumanReadableContains @@ -661,9 +735,12 @@ tasks: '#none#': - "18" scriptarguments: - extend-context: "" - humanReadableEntryId: ${lastCompletedTaskEntries.[0]} - substring: Lovely Day + extend-context: {} + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Lovely Day + separatecontext: false view: |- { "position": { @@ -671,12 +748,13 @@ tasks: "y": 1040 } } + note: false "37": id: "37" - taskid: 9cd33e05-b37d-4ecf-86d1-01b84209a557 + taskid: c75fc8dd-fe88-43b1-8926-2c98fd6743bb type: regular task: - id: 9cd33e05-b37d-4ecf-86d1-01b84209a557 + id: c75fc8dd-fe88-43b1-8926-2c98fd6743bb version: -1 name: 'Verify output contains: "Hello World"' scriptName: VerifyHumanReadableContains @@ -687,8 +765,11 @@ tasks: '#none#': - "25" scriptarguments: - humanReadableEntryId: ${lastCompletedTaskEntries.[0]} - substring: Hello World + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Hello World + separatecontext: false view: |- { "position": { @@ -696,12 +777,13 @@ tasks: "y": 1535 } } + note: false "40": id: "40" - taskid: 7340101e-6795-4a9a-86af-015af8a30fb4 + taskid: d2ddf2d4-a568-45cb-84ce-0a6939e57ba3 type: regular task: - id: 7340101e-6795-4a9a-86af-015af8a30fb4 + id: d2ddf2d4-a568-45cb-84ce-0a6939e57ba3 version: -1 name: 'Verify output contains: "Goodbye Please"' scriptName: VerifyHumanReadableContains @@ -712,8 +794,11 @@ tasks: '#none#': - "30" scriptarguments: - humanReadableEntryId: ${lastCompletedTaskEntries.[0]} - substring: Goodbye Please + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Goodbye Please + separatecontext: false view: |- { "position": { @@ -721,12 +806,13 @@ tasks: "y": 2410 } } + note: false "41": id: "41" - taskid: 26683302-d9d1-408f-8bba-99cce51f4f42 + taskid: 891181f3-e216-4d70-87b9-c1f3815ab22b type: regular task: - id: 26683302-d9d1-408f-8bba-99cce51f4f42 + id: 891181f3-e216-4d70-87b9-c1f3815ab22b version: -1 name: 'Verify output contains: "Lovely Day"' scriptName: VerifyHumanReadableContains @@ -737,8 +823,11 @@ tasks: '#none#': - "26" scriptarguments: - humanReadableEntryId: ${lastCompletedTaskEntries.[0]} - substring: Lovely Day + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Lovely Day + separatecontext: false view: |- { "position": { @@ -746,12 +835,13 @@ tasks: "y": 1885 } } + note: false "42": id: "42" - taskid: 1dc913b4-1e04-4fbf-8a95-28247b9a6cac + taskid: 574fd6e5-c63a-4e80-8ee0-86699dd4ac28 type: regular task: - id: 1dc913b4-1e04-4fbf-8a95-28247b9a6cac + id: 574fd6e5-c63a-4e80-8ee0-86699dd4ac28 version: -1 name: 'Verify output contains: "Goodbye Please"' scriptName: VerifyHumanReadableContains @@ -762,8 +852,11 @@ tasks: '#none#': - "31" scriptarguments: - humanReadableEntryId: ${lastCompletedTaskEntries.[0]} - substring: Goodbye Please + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Goodbye Please + separatecontext: false view: |- { "position": { @@ -771,12 +864,13 @@ tasks: "y": 2760 } } + note: false "43": id: "43" - taskid: fef69a10-21d1-40d1-81bf-84f8b17b93a8 + taskid: 33bea087-a196-476e-84c5-34eca5a30aa8 type: regular task: - id: fef69a10-21d1-40d1-81bf-84f8b17b93a8 + id: 33bea087-a196-476e-84c5-34eca5a30aa8 version: -1 name: 'Verify output contains: "Goodbye Please"' scriptName: VerifyHumanReadableContains @@ -787,8 +881,11 @@ tasks: '#none#': - "33" scriptarguments: - humanReadableEntryId: ${lastCompletedTaskEntries.[0]} - substring: Goodbye Please + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Goodbye Please + separatecontext: false view: |- { "position": { @@ -796,21 +893,28 @@ tasks: "y": 3110 } } + note: false "44": id: "44" - taskid: f43e97c5-95f2-481d-8a04-e1240e3e290a + taskid: 1881c0da-1a5e-478b-8420-4a259c3457eb type: regular task: - id: f43e97c5-95f2-481d-8a04-e1240e3e290a + id: 1881c0da-1a5e-478b-8420-4a259c3457eb version: -1 name: 'Verify output contains: "test_file.txt"' scriptName: VerifyHumanReadableContains type: regular iscommand: false brand: "" + nexttasks: + '#none#': + - "52" scriptarguments: - humanReadableEntryId: ${lastCompletedTaskEntries.[0]} - substring: test_file.txt + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: test_file.txt + separatecontext: false view: |- { "position": { @@ -818,14 +922,15 @@ tasks: "y": 3810 } } + note: false "45": id: "45" - taskid: 79ce047f-2b14-42d9-8296-782f3f421f7f + taskid: a40ccffd-feda-4d50-80ed-462e9e3844fc type: regular task: - id: 79ce047f-2b14-42d9-8296-782f3f421f7f + id: a40ccffd-feda-4d50-80ed-462e9e3844fc version: -1 - name: 'servicenow-upload-file test_file.txt to Ticket #2' + name: 'servicenow-upload-file test_file.txt to Ticket #0' script: ServiceNow|||servicenow-upload-file type: regular iscommand: true @@ -834,10 +939,13 @@ tasks: '#none#': - "44" scriptarguments: - file_id: ${File.[0].EntryID} - file_name: "" - id: ${Ticket.[2].ID} - ticket_type: "" + file_id: + simple: ${File.EntryID} + file_name: {} + id: + simple: ${Ticket.[0].ID} + ticket_type: {} + separatecontext: false view: |- { "position": { @@ -845,12 +953,13 @@ tasks: "y": 3635 } } + note: false "46": id: "46" - taskid: 894d67b2-be78-427e-86be-092a8728e19c + taskid: 18460c88-cefb-42c5-829e-b7ae3b067072 type: regular task: - id: 894d67b2-be78-427e-86be-092a8728e19c + id: 18460c88-cefb-42c5-829e-b7ae3b067072 version: -1 name: VerifyContextFields - File Uploaded to War Room scriptName: VerifyContextFields @@ -861,16 +970,19 @@ tasks: '#none#': - "45" scriptarguments: - field1: File.[0].Name - field2: "" - field3: "" - field4: "" - field5: "" - value1: test_file.txt - value2: "" - value3: "" - value4: "" - value5: "" + field1: + simple: File.Name + field2: {} + field3: {} + field4: {} + field5: {} + value1: + simple: test_file.txt + value2: {} + value3: {} + value4: {} + value5: {} + separatecontext: false view: |- { "position": { @@ -878,12 +990,293 @@ tasks: "y": 3460 } } + note: false + "47": + id: "47" + taskid: 771f1530-11aa-4f19-82aa-be984bf9567e + type: title + task: + id: 771f1530-11aa-4f19-82aa-be984bf9567e + version: -1 + name: Done + type: title + iscommand: false + brand: "" + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 5420 + } + } + note: false + "48": + id: "48" + taskid: 24703920-6f3c-4308-8d13-4446956af85b + type: regular + task: + id: 24703920-6f3c-4308-8d13-4446956af85b + version: -1 + name: servicenow-get-groups + description: Return information about specific servicenow group + script: ServiceNow|||servicenow-get-groups + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "49" + scriptarguments: + name: + simple: Incident Management + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 4510 + } + } + note: false + "49": + id: "49" + taskid: 8dcec7a9-9c6a-40ed-801d-d4df1d4192b1 + type: regular + task: + id: 8dcec7a9-9c6a-40ed-801d-d4df1d4192b1 + version: -1 + name: VerifyContext + description: |- + Verifies path in context: + - Verifies path existence + - If matching object is an array: verify fields exists in each of the objects in the array + - If matching object is not an array: verify fields exists in matching object + - if 'expectedValue' is given: ensure that the given value is equal to the context path + scriptName: VerifyContext + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "53" + scriptarguments: + expectedValue: {} + fields: {} + path: + simple: ServiceNowGroups + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 4685 + } + } + note: false + "50": + id: "50" + taskid: 7217c824-3a59-47b9-8708-db0d7e8db551 + type: regular + task: + id: 7217c824-3a59-47b9-8708-db0d7e8db551 + version: -1 + name: 'Get Ticket #0 with attachments' + description: Retrieve ticket information by specific ticket ID + script: ServiceNow|||servicenow-get + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "51" + scriptarguments: + get_attachments: + simple: "true" + id: + simple: ${Ticket.[0].ID} + number: {} + ticket_type: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 4160 + } + } + note: false + "51": + id: "51" + taskid: 7a195718-e882-4017-8e01-3cfcb9f07276 + type: regular + task: + id: 7a195718-e882-4017-8e01-3cfcb9f07276 + version: -1 + name: Verify File + description: |- + Verifies path in context: + - Verifies path existence + - If matching object is an array: verify fields exists in each of the objects in the array + - If matching object is not an array: verify fields exists in matching object + - if 'expectedValue' is given: ensure that the given value is equal to the context path + scriptName: VerifyContext + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "48" + scriptarguments: + expectedValue: {} + fields: {} + path: + simple: File + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 4335 + } + } + note: false + "52": + id: "52" + taskid: 825f7a30-af72-423b-89ff-fc1956af5045 + type: regular + task: + id: 825f7a30-af72-423b-89ff-fc1956af5045 + version: -1 + name: Delete File Context + description: Delete field from context + scriptName: DeleteContext + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "50" + scriptarguments: + all: {} + index: {} + key: + simple: File + keysToKeep: {} + subplaybook: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 3985 + } + } + note: false + "53": + id: "53" + taskid: 33e33025-0b89-4e41-8291-0ca44a6e4f90 + type: regular + task: + id: 33e33025-0b89-4e41-8291-0ca44a6e4f90 + version: -1 + name: servicenow-query-table + description: Query a specified table in ServiceNow + script: ServiceNow|||servicenow-query-table + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "54" + scriptarguments: + fields: + simple: sys_id,state,active + limit: {} + query: + simple: sys_idin${Ticket.ID} + table_name: + simple: incident + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 4860 + } + } + note: false + "54": + id: "54" + taskid: c3aa8dc0-adac-497a-830a-8d511a0c5491 + type: regular + task: + id: c3aa8dc0-adac-497a-830a-8d511a0c5491 + version: -1 + name: VerifyContext + description: |- + Verifies path in context: + - Verifies path existence + - If matching object is an array: verify fields exists in each of the objects in the array + - If matching object is not an array: verify fields exists in matching object + - if 'expectedValue' is given: ensure that the given value is equal to the context path + scriptName: VerifyContext + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "55" + scriptarguments: + expectedValue: {} + fields: + simple: sys_id,state,active + path: + simple: ServiceNow.Results + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 5035 + } + } + note: false + "55": + id: "55" + taskid: 18d47a02-871b-45af-898e-c11b2af5cfcc + type: regular + task: + id: 18d47a02-871b-45af-898e-c11b2af5cfcc + version: -1 + name: servicenow-delete-ticket + description: Delete a ticket from ServiceNow + script: ServiceNow|||servicenow-delete-ticket + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "47" + scriptarguments: + id: + simple: ${Ticket.ID} + ticket_type: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 5215 + } + } + note: false view: |- { "linkLabelsPosition": {}, "paper": { "dimensions": { - "height": 3855, + "height": 5435, "width": 810, "x": 50, "y": 50 @@ -891,3 +1284,4 @@ view: |- } } inputs: [] +outputs: [] From 306556d9d5758598744231b6aee818e7f8b02d43 Mon Sep 17 00:00:00 2001 From: Lior Blob Date: Sun, 28 Oct 2018 20:15:59 +0200 Subject: [PATCH 03/11] ticket/record, delete-ticket --- Integrations/integration-ServiceNow.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Integrations/integration-ServiceNow.yml b/Integrations/integration-ServiceNow.yml index e083866d3909..e5c52ccb798e 100644 --- a/Integrations/integration-ServiceNow.yml +++ b/Integrations/integration-ServiceNow.yml @@ -2711,11 +2711,12 @@ script: arguments: - name: table_name required: true + description: The table name - name: id required: true description: The system ID of the ticket to update description: Delete a record in a specified ServiceNow table isfetch: true -releaseNotes: "-" +releaseNotes: "" tests: - Run all tests From bbd20d65546fb68e90acb990ad624056b74f20fb Mon Sep 17 00:00:00 2001 From: Lior Blob Date: Wed, 31 Oct 2018 18:18:10 +0200 Subject: [PATCH 04/11] more generics + outputs --- Integrations/integration-ServiceNow.yml | 431 ++++++++++++++++-------- 1 file changed, 288 insertions(+), 143 deletions(-) diff --git a/Integrations/integration-ServiceNow.yml b/Integrations/integration-ServiceNow.yml index e5c52ccb798e..4039c283805a 100644 --- a/Integrations/integration-ServiceNow.yml +++ b/Integrations/integration-ServiceNow.yml @@ -158,6 +158,15 @@ script: "severity", "sla_due", "state", "subcategory", "sys_tags", "time_worked", "urgency", "user_input", "watch_list", "work_end", "work_notes", "work_notes_list", "work_start", "impact", "incident_state", "title", "type", "category", "state"] + + DEFAULT_RECORD_FIELDS = { + 'sys_id': 'ID', + 'sys_updated_by': 'UpdatedBy', + 'sys_updated_on': 'UpdatedAt', + 'sys_created_by': 'CreatedBy', + 'sys_created_on': 'CreatedAt' + } + ''' HELPER FUNCTIONS ''' def send_request(path, method='get', body=None, params=None, headers=None, file=None): @@ -282,6 +291,8 @@ script: return result def get_ticket_fields(template): + inv_severity = {v: k for k, v in TICKET_SEVERITY.iteritems()} + inv_priority = {v: k for k, v in TICKET_PRIORITY.iteritems()} body = {} for arg in SNOW_ARGS: input_arg = demisto.args().get(arg) @@ -299,8 +310,6 @@ script: def get_body(template, fields, custom_fields): body = {} - inv_severity = {v: k for k, v in TICKET_SEVERITY.iteritems()} - inv_priority = {v: k for k, v in TICKET_PRIORITY.iteritems()} if fields: for field in fields: @@ -366,7 +375,7 @@ script: number = demisto.args().get('number') get_attachments = demisto.args().get('get_attachments', 'false') - res = get_ticket(ticket_id, number, ticket_type) + res = get(ticket_type, ticket_id, number) if not res or 'result' not in res: return 'Cannot find ticket' @@ -399,14 +408,59 @@ script: return entries + def get_record_command(): + table_name = demisto.args()['table_name'] + record_id = demisto.args()['id'] + fields = demisto.args().get('fields') + + res = get(table_name, record_id) - def get_ticket(ticket_id, number, ticket_type): + if not res or 'result' not in res: + return 'Cannot find record' + + if isinstance(res['result'], list): + if len(res['result']) == 0: + return 'Cannot find ticket' + record = res['result'][0] + else: + record = res['result'] + + entry = { + 'Type': entryTypes['note'], + 'Contents': res, + 'ContentsFormat': formats['json'] + } + + if fields: + fields = argToList(fields) + record = dict(filter(lambda kv_pair: kv_pair[0] in fields, record.items())) + if record: + entry['ReadableContentsFormat'] = formats['markdown'] + entry['HumanReadable'] = tableToMarkdown('ServiceNow details for record', record, removeNull=True) + entry['EntryContext'] = { + 'ServiceNow.Record': createContext(record) + } + else: + entry['ReadableContentsFormat'] = formats['text'] + entry['HumanReadable'] = 'No such fields were found in the result' + else: + mapped_record = {DEFAULT_RECORD_FIELDS[k]: record[k] for k in DEFAULT_RECORD_FIELDS} + entry['ReadableContentsFormat'] = formats['markdown'] + entry['HumanReadable'] = tableToMarkdown('ServiceNow details for record' + record_id, mapped_record, removeNull=True) + entry['EntryContext'] = { + 'ServiceNow.Record': createContext(mapped_record) + } + + return entry + + + def get(table_name, record_id, number = None): path = None query_params = {} - if ticket_id: - path = 'table/' + ticket_type + '/' + ticket_id + if record_id: + path = 'table/' + table_name + '/' + record_id elif number: - path = 'table/' + ticket_type + path = 'table/' + table_name query_params = { 'number': number } @@ -415,7 +469,6 @@ script: return send_request(path, 'get', params = query_params) - def get_ticket_attachments(ticket_id): path = 'attachment' query_params = { @@ -464,10 +517,44 @@ script: return entry + def update_record_command(): + table_name = demisto.args()['table_name'] + record_id = demisto.args()['id'] + fields = demisto.args().get('fields') + custom_fields = demisto.args().get('custom_fields') + + if fields: + fields = split_fields(fields) + if custom_fields: + custom_fields = split_fields(custom_fields) + + res = update(table_name, record_id, fields, custom_fields) + + if not res or 'result' not in res: + return 'Cannot find record' + + result = res['result'] + + mapped_record = {DEFAULT_RECORD_FIELDS[k]: result[k] for k in DEFAULT_RECORD_FIELDS} + entry = { + 'Type': entryTypes['note'], + 'Contents': res, + 'ContentsFormat': formats['json'], + 'ReadableContentsFormat': formats['markdown'], + 'HumanReadable': tableToMarkdown('ServiceNow updated record', mapped_record, removeNull=True), + 'EntryContext': { + 'ServiceNow.Record': createContext(mapped_record) + } + } + + return entry + + res = update(table_name, record_id, fields, custom_fields) + - def update(ticket_type, ticket_id, fields, custom_fields, template): + def update(table_name, record_id, fields, custom_fields, template=None): body = get_body(template, fields, custom_fields) - path = 'table/' + ticket_type + '/' + ticket_id + path = 'table/' + table_name + '/' + record_id return send_request(path, 'patch', body = body) @@ -497,9 +584,42 @@ script: return entry - def create(ticket_type, fields, custom_fields, template): + def create_record_command(): + table_name = demisto.args()['table_name'] + fields = demisto.args().get('fields') + custom_fields = demisto.args().get('custom_fields') + template = demisto.args().get('template') + + if fields: + fields = split_fields(fields) + if custom_fields: + custom_fields = split_fields(custom_fields) + + res = create(table_name, fields, custom_fields, template) + + if not res or 'result' not in res: + return 'Cannot find record' + + result = res['result'] + + mapped_record = {DEFAULT_RECORD_FIELDS[k]: result[k] for k in DEFAULT_RECORD_FIELDS} + entry = { + 'Type': entryTypes['note'], + 'Contents': res, + 'ContentsFormat': formats['json'], + 'ReadableContentsFormat': formats['markdown'], + 'HumanReadable': tableToMarkdown('ServiceNow created record', mapped_record, removeNull=True), + 'EntryContext': { + 'ServiceNow.Record': createContext(mapped_record) + } + } + + return entry + + + def create(table_name, fields, custom_fields, template): body = get_body(template, fields, custom_fields) - path = 'table/' + ticket_type + path = 'table/' + table_name return send_request(path, 'post', body = body) @@ -519,11 +639,27 @@ script: return entry - def delete(ticket_type, ticket_id): - path = 'table/' + ticket_type + '/' + ticket_id + def delete_record_command(): + record_id = demisto.args()['id'] + table_name = demisto.args().get('table_name') - return send_request(path, 'delete') + res = delete(table_name, record_id) + entry = { + 'Type': entryTypes['note'], + 'Contents': res, + 'ContentsFormat': formats['json'], + 'ReadableContentsFormat': formats['text'], + 'HumanReadable': 'Record with ID ' + record_id + ' was succesfully deleted.' + } + + return entry + + + def delete(table_name, record_id): + path = 'table/' + table_name + '/' + record_id + + return send_request(path, 'delete') def add_link_command(): @@ -631,17 +767,22 @@ script: if fields: fields = argToList(fields) result = [dict(filter(lambda kv_pair: kv_pair[0] in fields, r.iteritems())) for r in res['result']] - - if any(result): + if any(result): + entry['ReadableContentsFormat'] = formats['markdown'] + entry['HumanReadable'] = tableToMarkdown('ServiceNow query results', result, removeNull=True) + entry['EntryContext'] = { + 'ServiceNow.Results': createContext(result) + } + else: + entry['ReadableContentsFormat'] = formats['text'] + entry['HumanReadable'] = 'No such fields were found in the result' + else: + mapped_records = [{DEFAULT_RECORD_FIELDS[k]: r[k] for k in DEFAULT_RECORD_FIELDS} for r in result] entry['ReadableContentsFormat'] = formats['markdown'] - entry['HumanReadable'] = tableToMarkdown('ServiceNow query results for table: ' + table_name, result, removeNull=True) + entry['HumanReadable'] = tableToMarkdown('ServiceNow query results', mapped_records, removeNull=True) entry['EntryContext'] = { - 'ServiceNow.Results': createContext(result) + 'ServiceNow.Results': createContext(mapped_records) } - else: - entry['ReadableContentsFormat'] = formats['text'] - entry['HumanReadable'] = 'No such fields were found in the result' - return entry @@ -712,10 +853,10 @@ script: def get_computer_command(): - ticket_type = 'cmdb_ci_computer' + table_name = 'cmdb_ci_computer' computer_name = demisto.args()['computerName'] - res = get_computer(ticket_type, computer_name) + res = query(table_name, None, 'u_code=' + computer_name) if not res or 'result' not in res: return 'Cannot find computer' @@ -755,19 +896,10 @@ script: return entry - def get_computer(ticket_type, computer_name): - path = 'table/' + ticket_type - - query_params = { - 'sysparm_query': 'u_code=' + computer_name - } - - return send_request(path, 'get', params=query_params) - def get_groups_command(): - ticket_type = 'sys_user_group' + table_name = 'sys_user_group' group_name = demisto.args()['name'] - res = get_groups(ticket_type, group_name) + res = query(table_name, None, 'name=' + group_name) if not res or 'result' not in res: return 'Cannot find groups' @@ -803,14 +935,6 @@ script: return entry - def get_groups(ticket_type, group_name): - path = 'table/' + ticket_type - - query_params = { - 'sysparm_query': 'name=' + group_name - } - - return send_request(path, 'get', params=query_params) def fetch_incidents(): query_params = {} @@ -887,7 +1011,7 @@ script: demisto.results('ok') elif demisto.command() == 'fetch-incidents': fetch_incidents() - elif demisto.command() == 'servicenow-get' or demisto.command() == 'servicenow-incident-update': + elif demisto.command() == 'servicenow-get' or demisto.command() == 'servicenow-incident-update' or demisto.command() == 'servicenow-get-ticket': demisto.results(get_ticket_command()) elif demisto.command() == 'servicenow-update' or demisto.command() == 'servicenow-incident-update' or demisto.command() == 'servicenow-update-ticket': demisto.results(update_ticket_command()) @@ -907,6 +1031,14 @@ script: demisto.results(get_computer_command()) elif demisto.command() == 'servicenow-get-groups': demisto.results(get_groups_command()) + elif demisto.command() == 'servicenow-get-record': + demisto.results(get_record_command()) + elif demisto.command() == 'servicenow-update-record': + demisto.results(update_record_command()) + elif demisto.command() == 'servicenow-create-record': + demisto.results(create_record_command()) + elif demisto.command() == 'servicenow-delete-record': + demisto.results(delete_record_command()) if demisto.command() == 'servicenow-query-table': demisto.results(query_table_command()) except Exception as e: @@ -1076,7 +1208,8 @@ script: - contextPath: File.MD5 description: Attachment file MD5 type: string - description: Retrieve ticket information by specific ticket ID + description: Deprecated. Please use servicenow-get-ticket or servicenow-get-record + instead - name: servicenow-incident-get deprecated: true arguments: @@ -1130,23 +1263,23 @@ script: - name: urgency auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket urgency - name: severity auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket severity - name: impact auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket impact - name: active auto: PREDEFINED @@ -1283,11 +1416,11 @@ script: - name: priority auto: PREDEFINED predefined: - - Planning - - Low - - Moderate - - High - - Critical + - 5 - Planning + - 4 - Low + - 3 - Moderate + - 2 - High + - 1 - Critical description: Priority of the ticket (number) - name: template description: Template name to use as a base to create new tickets. @@ -1341,23 +1474,23 @@ script: - name: urgency auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket urgency - name: severity auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket severity - name: impact auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket impact - name: active auto: PREDEFINED @@ -1494,11 +1627,11 @@ script: - name: priority auto: PREDEFINED predefined: - - Planning - - Low - - Moderate - - High - - Critical + - 5 - Planning + - 4 - Low + - 3 - Moderate + - 2 - High + - 1 - Critical description: Priority of the ticket (number) - name: template description: Template name to use as a base to create new tickets. @@ -1533,7 +1666,8 @@ script: - contextPath: Ticket.Number description: ServiceNow ticket number type: string - description: Create new ServiceNow ticket + description: Deprecated. Please use servicenow-create-ticket or servicenow-create-record + instead - name: servicenow-incident-create deprecated: true arguments: @@ -1726,23 +1860,23 @@ script: - name: urgency auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket urgency - name: severity auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket severity - name: impact auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket impact - name: active auto: PREDEFINED @@ -1879,11 +2013,11 @@ script: - name: priority auto: PREDEFINED predefined: - - Planning - - Low - - Moderate - - High - - Critical + - 5 - Planning + - 4 - Low + - 3 - Moderate + - 2 - High + - 1 - Critical description: Priority of the ticket - name: id required: true @@ -1919,23 +2053,23 @@ script: - name: urgency auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket urgency - name: severity auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket severity - name: impact auto: PREDEFINED predefined: - - Low - - Medium - - High + - 3 - Low + - 2 - Medium + - 1 - High description: Ticket impact - name: active auto: PREDEFINED @@ -2072,11 +2206,11 @@ script: - name: priority auto: PREDEFINED predefined: - - Planning - - Low - - Moderate - - High - - Critical + - 5 - Planning + - 4 - Low + - 3 - Moderate + - 2 - High + - 1 - Critical description: Priority of the ticket - name: id required: true @@ -2093,7 +2227,8 @@ script: defaultValue: normal - name: state description: State of the ticket - description: Update specific ticket by providing ticket id + description: Deprecated. Please use servicenow-update-ticket or servicenow-update-record + instead - name: servicenow-incident-update deprecated: true arguments: @@ -2591,6 +2726,35 @@ script: description: Comments type: string description: query the cmdb_ci_computer table with a computer code + - name: servicenow-get-record + arguments: + - name: id + required: true + description: Record system ID + - name: fields + description: Table fields to display and output to the context + - name: table_name + required: true + description: The name of the table to get the record from + outputs: + - contextPath: ServiceNow.Record.ID + description: The unique record identifier for the record. + type: string + - contextPath: ServiceNow.Record.UpdatedBy + description: A string field that indicates the user who most recently updated + the record. + type: string + - contextPath: ServiceNow.Record.UpdatedAt + description: A time-stamp field that indicates the date and time of the most + recent update. + type: date + - contextPath: ServiceNow.Record.CreatedBy + description: A string field that indicates the user who created the record. + type: string + - contextPath: ServiceNow.Record.CreatedOn + description: time-stamp field that indicates when a record was created. + type: date + description: Retrieve record information by specific record ID - name: servicenow-query-table arguments: - name: table_name @@ -2606,31 +2770,23 @@ script: description: Table fields to display and output to the context isArray: true outputs: - - contextPath: ServiceNow.Results.sys_id + - contextPath: ServiceNow.Results.ID description: The unique record identifier for the record. type: string - - contextPath: ServiceNow.Results.sys_updated_by + - contextPath: ServiceNow.Results.UpdatedBy description: A string field that indicates the user who most recently updated the record. type: string - - contextPath: ServiceNow.Results.sys_updated + - contextPath: ServiceNow.Results.UpdatedAt description: A time-stamp field that indicates the date and time of the most recent update. type: date - - contextPath: ServiceNow.Results.sys_created_by + - contextPath: ServiceNow.Results.CreatedBy description: A string field that indicates the user who created the record. type: string - - contextPath: ServiceNow.Results.sys_created_on + - contextPath: ServiceNow.Results.CreatedOn description: time-stamp field that indicates when a record was created. type: date - - contextPath: ServiceNow.Results.sys_mod_count - description: A numeric field that counts the number of updates for this record - since record creation. - type: number - - contextPath: ServiceNow.Results.sys_class_name - description: If the table is extensible, a string field that indicates which - child table contains the record. - type: string description: Query a specified table in ServiceNow - name: servicenow-create-record arguments: @@ -2642,32 +2798,26 @@ script: fieldname1=value;fieldname2=value;...' - name: custom_fields description: 'Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;...' + - name: template + description: Template name to use as a base to create new tickets. outputs: - - contextPath: ServiceNow.Record.sys_id + - contextPath: ServiceNow.Record.ID description: The unique record identifier for the record. type: string - - contextPath: ServiceNow.Record.sys_updated_by + - contextPath: ServiceNow.Record.UpdatedBy description: A string field that indicates the user who most recently updated the record. type: string - - contextPath: ServiceNow.Record.sys_updated + - contextPath: ServiceNow.Record.UpdatedAt description: A time-stamp field that indicates the date and time of the most recent update. type: date - - contextPath: ServiceNow.Record.sys_created_by + - contextPath: ServiceNow.Record.CreatedBy description: A string field that indicates the user who created the record. type: string - - contextPath: ServiceNow.Record.sys_created_on + - contextPath: ServiceNow.Record.CreatedOn description: time-stamp field that indicates when a record was created. type: date - - contextPath: ServiceNow.Record.sys_mod_count - description: A numeric field that counts the number of updates for this record - since record creation. - type: number - - contextPath: ServiceNow.Record.sys_class_name - description: If the table is extensible, a string field that indicates which - child table contains the record. - type: string description: Create a new record in a specified ServiceNow table - name: servicenow-update-record arguments: @@ -2680,32 +2830,27 @@ script: - name: fields description: 'Fields and their values to update in the record, in the format: fieldname1=value;fieldname2=value;...' + - name: custom_fields + description: 'Custom(User defined) fields and their values to update in the + record, in the format: fieldname1=value;fieldname2=value;...' outputs: - - contextPath: ServiceNow.Record.sys_id + - contextPath: ServiceNow.Record.ID description: The unique record identifier for the record. type: string - - contextPath: ServiceNow.Record.sys_updated_by + - contextPath: ServiceNow.Record.UpdatedBy description: A string field that indicates the user who most recently updated the record. type: string - - contextPath: ServiceNow.Record.sys_updated + - contextPath: ServiceNow.Record.UpdatedAt description: A time-stamp field that indicates the date and time of the most recent update. type: date - - contextPath: ServiceNow.Record.sys_created_by + - contextPath: ServiceNow.Record.CreatedBy description: A string field that indicates the user who created the record. type: string - - contextPath: ServiceNow.Record.sys_created_on + - contextPath: ServiceNow.Record.CreatedOn description: time-stamp field that indicates when a record was created. type: date - - contextPath: ServiceNow.Record.sys_mod_count - description: A numeric field that counts the number of updates for this record - since record creation. - type: number - - contextPath: ServiceNow.Record.sys_class_name - description: If the table is extensible, a string field that indicates which - child table contains the record. - type: string description: Update a record in a specified ServiceNow table - name: servicenow-delete-record arguments: From ea75736e263f871254d56e836af7e5c68cccd91b Mon Sep 17 00:00:00 2001 From: Lior Blob Date: Mon, 26 Nov 2018 17:57:17 +0200 Subject: [PATCH 05/11] new commands --- Integrations/integration-ServiceNow.yml | 356 +++++++++++++++++++++++- 1 file changed, 345 insertions(+), 11 deletions(-) diff --git a/Integrations/integration-ServiceNow.yml b/Integrations/integration-ServiceNow.yml index 4039c283805a..3c1fe758fd8d 100644 --- a/Integrations/integration-ServiceNow.yml +++ b/Integrations/integration-ServiceNow.yml @@ -87,7 +87,7 @@ script: url = re.sub('\/$', '', url) return url - ''' GLOBAL VARIABLESS ''' + ''' GLOBAL VARIABLES ''' USERNAME = demisto.params()['credentials']['identifier'] PASSWORD = demisto.params()['credentials']['password'] VERIFY_SSL = not demisto.params().get('insecure', False) @@ -145,6 +145,15 @@ script: '5': '5 - Planning' } + COMPUTER_STATUS = { + '1': 'In use', + '2': 'On order', + '3': 'On maintenance', + '6': 'In stock/In transit', + '7': 'Retried', + '100': 'Missing' + } + DEFAULTS = { 'limit':10, 'fetch_limit':10 @@ -257,7 +266,7 @@ script: for ticket in tickets: hr = { - 'Number': ticket['number'], + 'Number': ticket.get('number'), 'System ID': ticket['sys_id'], 'Created On': ticket.get('sys_created_on'), 'Created By': ticket.get('sys_created_by'), @@ -420,7 +429,7 @@ script: if isinstance(res['result'], list): if len(res['result']) == 0: - return 'Cannot find ticket' + return 'Cannot find record' record = res['result'][0] else: record = res['result'] @@ -896,6 +905,157 @@ script: return entry + def query_computers_command(): + table_name = 'cmdb_ci_computer' + computer_id = demisto.args().get('computer_id') + computer_name = demisto.args().get('computer_name') + asset_tag = demisto.args().get('asset_tag') + computer_query = demisto.args().get('query', {}) + limit = demisto.args().get('limit') + + if computer_id: + res = get(table_name, computer_id) + else: + if computer_name: + computer_query = 'name=' + computer_name + elif asset_tag: + computer_query = 'asset_tag=' + asset_tag + + res = query(table_name, limit, computer_query) + + if not res or 'result' not in res: + return 'Cannot find computers' + + computers = res['result'] + if not isinstance(computers, list): + computers = [computers] + + if len(computers) == 0: + return 'Cannot find computers' + + mapped_computers = [{ + 'ID': computer['sys_id'], + 'AssetTag': computer['asset_tag'], + 'Name': computer['name'], + 'DisplayName': '{} - {}'.format(computer['asset_tag'], computer['name']), + 'SupportGroup': computer['support_group'], + 'OperatingSystem': computer['os'], + 'Company': computer['company'], + 'AssignedTo': computer['assigned_to'], + 'State': COMPUTER_STATUS.get(computer['install_status'], computer['install_status']), + 'Cost': '{} {}'.format(computer['cost'], computer['cost_cc']), + 'Comments': computer['comments'] + } for computer in computers] + + entry = { + 'Type': entryTypes['note'], + 'Contents': res, + 'ContentsFormat': formats['json'], + 'ReadableContentsFormat': formats['markdown'], + 'HumanReadable': tableToMarkdown('ServiceNow Computer', mapped_computers, removeNull=True), + 'EntryContext': { + 'ServiceNow.Computer(val.ID==obj.ID)': createContext(mapped_computers, removeNull=True), + } + } + + return entry + + + + def query_groups_command(): + table_name = 'sys_user_group' + group_id = demisto.args().get('group_id') + group_name = demisto.args().get('group_name') + group_query = demisto.args().get('query', {}) + limit = demisto.args().get('limit') + + if group_id: + res = get(table_name, group_id) + else: + if group_name: + group_query = 'name=' + group_name + res = query(table_name, limit, group_query) + + if not res or 'result' not in res: + return 'Cannot find groups' + + groups = res['result'] + if not isinstance(groups, list): + groups = [groups] + + if len(groups) == 0: + return 'Cannot find groups' + + mapped_groups = [{ + 'ID': group['sys_id'], + 'Description': group['description'], + 'Name': group['name'], + 'Active': group['active'], + 'Manager': group['manager'], + 'Updated': group['sys_updated_on'], + } for group in groups] + + entry = { + 'Type': entryTypes['note'], + 'Contents': res, + 'ContentsFormat': formats['json'], + 'ReadableContentsFormat': formats['markdown'], + 'HumanReadable': tableToMarkdown('ServiceNow Groups', mapped_groups, removeNull=True), + 'EntryContext': { + 'ServiceNow.Group(val.ID==obj.ID)': createContext(mapped_groups, removeNull=True), + } + } + + return entry + + + def query_users_command(): + table_name = 'sys_user' + user_id = demisto.args().get('user_id') + user_name = demisto.args().get('user_name') + user_query = demisto.args().get('query', {}) + limit = demisto.args().get('limit') + + if user_id: + res = get(table_name, user_id) + else: + if user_name: + user_query = 'user_name=' + user_name + res = query(table_name, limit, user_query) + + if not res or 'result' not in res: + return 'Cannot find users' + + users = res['result'] + if not isinstance(users, list): + users = [users] + + if len(users) == 0: + return 'Cannot find users' + + mapped_users = [{ + 'ID': user['sys_id'], + 'Name': '{} {} {}'.format(user['first_name'], user['middle_name'], user['last_name']), + 'UserName': user['user_name'], + 'Email': user['email'], + 'Created': user['sys_created_on'], + 'Updated': user['sys_updated_on'], + } for user in users] + + entry = { + 'Type': entryTypes['note'], + 'Contents': res, + 'ContentsFormat': formats['json'], + 'ReadableContentsFormat': formats['markdown'], + 'HumanReadable': tableToMarkdown('ServiceNow Users', mapped_users, removeNull=True), + 'EntryContext': { + 'ServiceNow.User(val.ID==obj.ID)': createContext(mapped_users, removeNull=True), + } + } + + return entry + + def get_groups_command(): table_name = 'sys_user_group' group_name = demisto.args()['name'] @@ -935,6 +1095,39 @@ script: return entry + def list_table_fields_command(): + table_name = demisto.args()['table_name'] + + res = get_table_fields(table_name) + + if not res or 'result' not in res: + return 'Cannot find table' + + if len(res['result']) == 0: + return 'Table contains no records' + + fields = [{'Name': k} for k,v in res['result'][0].iteritems()] + + entry = { + 'Type': entryTypes['note'], + 'Contents': res, + 'ContentsFormat': formats['json'], + 'ReadableContentsFormat': formats['markdown'], + 'HumanReadable': tableToMarkdown('ServiceNow Table fields - ' + table_name, fields), + 'EntryContext': { + 'ServiceNow.Field': createContext(fields), + } + } + + return entry + + def get_table_fields(table_name): + # Get 1 record + path = 'table/' + table_name + '?sysparm_limit=1' + res = send_request(path, 'GET') + + return res + def fetch_incidents(): query_params = {} @@ -1029,6 +1222,12 @@ script: demisto.results(upload_file_command()) elif demisto.command() == 'servicenow-get-computer': demisto.results(get_computer_command()) + elif demisto.command() == 'servicenow-query-computers': + demisto.results(query_computers_command()) + elif demisto.command() == 'servicenow-query-groups': + demisto.results(query_groups_command()) + elif demisto.command() == 'servicenow-query-users': + demisto.results(query_users_command()) elif demisto.command() == 'servicenow-get-groups': demisto.results(get_groups_command()) elif demisto.command() == 'servicenow-get-record': @@ -1039,8 +1238,8 @@ script: demisto.results(create_record_command()) elif demisto.command() == 'servicenow-delete-record': demisto.results(delete_record_command()) - if demisto.command() == 'servicenow-query-table': - demisto.results(query_table_command()) + if demisto.command() == 'servicenow-list-table-fields': + demisto.results(list_table_fields_command()) except Exception as e: LOG(e) LOG.print_log() @@ -1312,6 +1511,15 @@ script: - name: caused_by description: UID Format - name: close_code + auto: PREDEFINED + predefined: + - Solved (Work Around) + - Solved (Permanently) + - Solved Remotely (Work Around) + - Solved Remotely (Permanently) + - Not Solved (Not Reproducible) + - Not Solved (Too Costly) + - Closed/Resolved by Caller description: Ticket's close code - name: close_notes description: Close notes of the ticket @@ -1909,6 +2117,15 @@ script: - name: caused_by description: UID Format - name: close_code + auto: PREDEFINED + predefined: + - Solved (Work Around) + - Solved (Permanently) + - Solved Remotely (Work Around) + - Solved Remotely (Permanently) + - Not Solved (Not Reproducible) + - Not Solved (Too Costly) + - Closed/Resolved by Caller description: Ticket's close code - name: close_notes description: Close notes of the ticket @@ -2792,14 +3009,14 @@ script: arguments: - name: table_name required: true - description: The name of the table to create a record in + description: The name of the table to create a record in. - name: fields description: 'Fields and their values to create the record with, in the format: fieldname1=value;fieldname2=value;...' - name: custom_fields description: 'Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;...' - name: template - description: Template name to use as a base to create new tickets. + description: Template name to use as a base to create new records. outputs: - contextPath: ServiceNow.Record.ID description: The unique record identifier for the record. @@ -2859,9 +3076,126 @@ script: description: The table name - name: id required: true - description: The system ID of the ticket to update + description: The system ID of the ticket to delete description: Delete a record in a specified ServiceNow table + - name: servicenow-list-table-fields + arguments: + - name: table_name + required: true + description: Table name + outputs: + - contextPath: ServiceNow.Field + description: Table API field name + type: string + description: List API fields for a specified ServiceNow table + - name: servicenow-query-computers + arguments: + - name: computer_id + description: Query by computer sys_id + - name: computer_name + description: Query by computer name + - name: query + description: Query by specified query, for more information about querying in + ServiceNow, see https://docs.servicenow.com/bundle/istanbul-servicenow-platform/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html + - name: asset_tag + description: Query by asset tag + - name: limit + description: Query results limit + defaultValue: "10" + outputs: + - contextPath: ServiceNow.Computer.ID + description: Computer sys_id + type: string + - contextPath: ServiceNow.Computer.AssetTag + description: Computer Asset tag + type: string + - contextPath: ServiceNow.Computer.Name + description: Computer name + type: string + - contextPath: ServiceNow.Computer.DisplayName + description: Computer display name + type: string + - contextPath: ServiceNow.Computer.SupportGroup + description: Computer support group + type: string + - contextPath: ServiceNow.Computer.OperatingSystem + description: Computer operating system + type: string + - contextPath: ServiceNow.Computer.Company + description: Computer company sys_id + type: string + - contextPath: ServiceNow.Computer.AssignedTo + description: Computer assigned to user sys_id + type: string + - contextPath: ServiceNow.Computer.State + description: Computer state + type: string + - contextPath: ServiceNow.Computer.Cost + description: Computer cost + type: string + - contextPath: ServiceNow.Computer.Comments + description: Computer comments + type: string + description: Query the cmdb_ci_computer table in ServiceNow + - name: servicenow-query-groups + arguments: + - name: group_id + description: Query by group sys_id + - name: group_name + description: Query by group name + - name: query + description: Query by specified query, for more information about querying in + ServiceNow, see https://docs.servicenow.com/bundle/istanbul-servicenow-platform/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html + - name: limit + description: Query results limit + outputs: + - contextPath: ServiceNow.Group.ID + description: Group sys_id + type: string + - contextPath: ServiceNow.Group.Description + description: Group description + type: string + - contextPath: ServiceNow.Group.Name + description: Group name + type: string + - contextPath: ServiceNow.Group.Manager + description: Group manager sys_id + type: string + - contextPath: ServiceNow.Group.Updated + description: Group update time + type: date + description: Query the sys_user_group table in ServiceNow + - name: servicenow-query-users + arguments: + - name: user_id + description: Query by user sys_id + - name: user_name + description: Query by username + - name: query + description: Query by specified query, for more information about querying in + ServiceNow, see https://docs.servicenow.com/bundle/istanbul-servicenow-platform/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html + - name: limit + description: Query results limit + outputs: + - contextPath: ServiceNow.User.ID + description: User sys_id + type: string + - contextPath: ServiceNow.User.Name + description: User name (first + middle + last) + type: string + - contextPath: ServiceNow.User.UserName + description: User username + type: string + - contextPath: ServiceNow.User.Email + description: User email + type: string + - contextPath: ServiceNow.User.Created + description: User creation time + type: date + - contextPath: ServiceNow.User.Updated + description: User update time + type: date + description: Query the sys_user table in ServiceNow isfetch: true -releaseNotes: "" -tests: - - Run all tests + runonce: false +releaseNotes: "" \ No newline at end of file From 18d889d99c4066cc5382f27ee3fe2f050ec5a30d Mon Sep 17 00:00:00 2001 From: Lior Blob Date: Tue, 27 Nov 2018 10:16:22 +0200 Subject: [PATCH 06/11] fixes #1 --- Integrations/integration-ServiceNow.yml | 239 +++++++++++++++--------- 1 file changed, 150 insertions(+), 89 deletions(-) diff --git a/Integrations/integration-ServiceNow.yml b/Integrations/integration-ServiceNow.yml index 3c1fe758fd8d..0dc551533140 100644 --- a/Integrations/integration-ServiceNow.yml +++ b/Integrations/integration-ServiceNow.yml @@ -165,7 +165,7 @@ script: "delivery_plan", "delivery_task", "description", "due_date", "expected_start", "follow_up", "group_list", "hold_reason", "impact", "incident_state", "knowledge", "location", "made_sla", "notify", "order", "parent", "parent_incident", "priority", "problem_id", "resolved_at", "resolved_by", "rfc", "severity", "sla_due", "state", "subcategory", "sys_tags", "time_worked", "urgency", "user_input", "watch_list", "work_end", "work_notes", "work_notes_list", - "work_start", "impact", "incident_state", "title", "type", "category", "state"] + "work_start", "impact", "incident_state", "title", "type", "change_type", "category", "state"] DEFAULT_RECORD_FIELDS = { @@ -230,32 +230,41 @@ script: else: return 'incident' - def create_ticket_context(data): + def create_ticket_context(data, ticket_type): context = { 'ID': data["sys_id"], - 'State': data.get('state'), 'Summary': data.get('short_description'), 'Number': data.get('number'), 'CreatedOn': data.get('sys_created_on'), + 'Active': data.get('active'), 'AdditionalComments': data.get('comments'), - 'Priority': data.get('priority'), - 'OpenedAt': data.get('opened') + 'CloseCode': data.get('close_code'), + 'OpenedAt': data.get('opened_at') } + if 'closed_by' in data: + context['ResolvedBy'] = data["closed_by"]['value'] if 'value' in data['closed_by'] else '' if 'opened_by' in data: - context['Creator'] = data["opened_by"]['value'] if 'value' in data['opened_by'] else '' + context['OpenedBy'] = data["opened_by"]['value'] if 'value' in data['opened_by'] else '' if 'assigned_to' in data: context['Assignee'] = data["assigned_to"]['value'] if 'value' in data['assigned_to'] else '' + if 'priority' in data: + context['Priority'] = TICKET_PRIORITY.get(data['priority'], data['priority']) + if 'state' in data: + mapped_state = data['state'] + if ticket_type in TICKET_STATES: + mapped_state = TICKET_STATES[ticket_type].get(data['state'], mapped_state) + context['State'] = mapped_state return createContext(context, removeNull=True) - def get_ticket_context(data): + def get_ticket_context(data, ticket_type): if not isinstance(data, list): - return create_ticket_context(data) + return create_ticket_context(data, ticket_type) tickets = [] for d in data: - tickets.append(create_ticket_context(d)) + tickets.append(create_ticket_context(d, ticket_type)) return tickets def get_ticket_human_readable(tickets, ticket_type): @@ -272,10 +281,11 @@ script: 'Created By': ticket.get('sys_created_by'), 'Active': ticket.get('active'), 'Close notes': ticket.get('close_notes'), + 'Close code': ticket.get('close_code'), 'Description': ticket.get('description'), 'Opened at': ticket.get('opened_at'), 'Due date': ticket.get('due_date'), - 'Resolved by': ticket.get('closed_by'), + 'Resolved by': ticket.get('closed_by', {}).get('value'), 'Resolved at': ticket.get('resolved_at'), 'SLA due': ticket.get('sla_due'), 'Short description': ticket.get('short_description'), @@ -401,15 +411,21 @@ script: entries = get_ticket_attachment_entries(ticket['sys_id']) hr = get_ticket_human_readable(ticket, ticket_type) + context = get_ticket_context(ticket, ticket_type) + + headers = ['System ID','Number','Impact','Urgency','Severity','Priority','State','Created On','Created By','Active','Close notes','Close code', + 'Description','Opened at','Due date','Resolved by','Resolved at','SLA due','Short description','Additional Comments'] + entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('ServiceNow ticket', hr, removeNull=True), + 'HumanReadable': tableToMarkdown('ServiceNow ticket', hr, headers=headers, removeNull=True), 'EntryContext': { - 'Ticket(val.ID==obj.ID)': get_ticket_context(ticket) + 'Ticket(val.ID==obj.ID)': context, + 'ServiceNow.Ticket(val.ID==obj.ID)': context } } @@ -442,22 +458,24 @@ script: if fields: fields = argToList(fields) + if 'sys_id' not in fields: + fields.append('sys_id') record = dict(filter(lambda kv_pair: kv_pair[0] in fields, record.items())) - if record: - entry['ReadableContentsFormat'] = formats['markdown'] - entry['HumanReadable'] = tableToMarkdown('ServiceNow details for record', record, removeNull=True) - entry['EntryContext'] = { - 'ServiceNow.Record': createContext(record) - } - else: - entry['ReadableContentsFormat'] = formats['text'] - entry['HumanReadable'] = 'No such fields were found in the result' + for k,v in record.iteritems(): + if isinstance(v, dict): + record[k] = v.get('value', record[k]) + record['ID'] = record.pop('sys_id') + entry['ReadableContentsFormat'] = formats['markdown'] + entry['HumanReadable'] = tableToMarkdown('ServiceNow record', record, removeNull=True) + entry['EntryContext'] = { + 'ServiceNow.Record(val.ID==obj.ID)': createContext(record) + } else: mapped_record = {DEFAULT_RECORD_FIELDS[k]: record[k] for k in DEFAULT_RECORD_FIELDS} entry['ReadableContentsFormat'] = formats['markdown'] - entry['HumanReadable'] = tableToMarkdown('ServiceNow details for record' + record_id, mapped_record, removeNull=True) + entry['HumanReadable'] = tableToMarkdown('ServiceNow record' + record_id, mapped_record, removeNull=True) entry['EntryContext'] = { - 'ServiceNow.Record': createContext(mapped_record) + 'ServiceNow.Record(val.ID==obj.ID)': createContext(mapped_record) } return entry @@ -521,7 +539,7 @@ script: 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('ServiceNow ticket updated\nTicket type: ' + ticket_type, hr, removeNull=True) + 'HumanReadable': tableToMarkdown('ServiceNow ticket updated successfully\nTicket type: ' + ticket_type, hr, removeNull=True) } return entry @@ -529,7 +547,7 @@ script: def update_record_command(): table_name = demisto.args()['table_name'] record_id = demisto.args()['id'] - fields = demisto.args().get('fields') + fields = demisto.args().get('fields', {}) custom_fields = demisto.args().get('custom_fields') if fields: @@ -540,7 +558,7 @@ script: res = update(table_name, record_id, fields, custom_fields) if not res or 'result' not in res: - return 'Cannot find record' + return 'Could not retrieve record' result = res['result'] @@ -552,7 +570,7 @@ script: 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow updated record', mapped_record, removeNull=True), 'EntryContext': { - 'ServiceNow.Record': createContext(mapped_record) + 'ServiceNow.Record(val.ID==obj.ID)': createContext(mapped_record) } } @@ -579,15 +597,20 @@ script: res = create(ticket_type, fields, custom_fields, template) hr = get_ticket_human_readable(res['result'], ticket_type) + context = get_ticket_context(ticket, ticket_type) + + headers = ['System ID','Number','Impact','Urgency','Severity','Priority','State','Created On','Created By','Active','Close notes','Close code', + 'Description','Opened at','Due date','Resolved by','Resolved at','SLA due','Short description','Additional Comments'] entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('ServiceNow ticket created\nTicket type: ' + ticket_type, hr, removeNull=True), + 'HumanReadable': tableToMarkdown('ServiceNow ticket created successfully', hr, headers=headers, removeNull=True), 'EntryContext': { - 'Ticket(val.ID==obj.ID)': get_ticket_context(res['result']) + 'Ticket(val.ID==obj.ID)': context, + 'ServiceNow.Ticket(val.ID==obj.ID)': context } } @@ -607,7 +630,7 @@ script: res = create(table_name, fields, custom_fields, template) if not res or 'result' not in res: - return 'Cannot find record' + return 'Could not retrieve record' result = res['result'] @@ -619,7 +642,7 @@ script: 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow created record', mapped_record, removeNull=True), 'EntryContext': { - 'ServiceNow.Record': createContext(mapped_record) + 'ServiceNow.Record(val.ID==obj.ID)': createContext(mapped_record) } } @@ -739,15 +762,20 @@ script: return 'No results found' hr = get_ticket_human_readable(res['result'], ticket_type) + context = get_ticket_context(res['result'], ticket_type) + + headers = ['System ID','Number','Impact','Urgency','Severity','Priority','State','Created On','Created By','Active','Close notes','Close code', + 'Description','Opened at','Due date','Resolved by','Resolved at','SLA due','Short description','Additional Comments'] entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('ServiceNow ticket', hr, removeNull=True), + 'HumanReadable': tableToMarkdown('ServiceNow tickets', hr, headers=headers, removeNull=True), 'EntryContext': { - 'Ticket(val.ID==obj.ID)': get_ticket_context(res['result']) + 'Ticket(val.ID==obj.ID)': context, + 'ServiceNow.Ticket(val.ID==obj.ID)': context } } @@ -775,22 +803,25 @@ script: if fields: fields = argToList(fields) - result = [dict(filter(lambda kv_pair: kv_pair[0] in fields, r.iteritems())) for r in res['result']] - if any(result): - entry['ReadableContentsFormat'] = formats['markdown'] - entry['HumanReadable'] = tableToMarkdown('ServiceNow query results', result, removeNull=True) - entry['EntryContext'] = { - 'ServiceNow.Results': createContext(result) - } - else: - entry['ReadableContentsFormat'] = formats['text'] - entry['HumanReadable'] = 'No such fields were found in the result' + if 'sys_id' not in fields: + fields.append('sys_id') + records = [dict(filter(lambda kv_pair: kv_pair[0] in fields, r.iteritems())) for r in res['result']] + for r in records: + r['ID'] = r.pop('sys_id') + for k,v in r.iteritems(): + if isinstance(v, dict): + r[k] = v.get('value', v) + entry['ReadableContentsFormat'] = formats['markdown'] + entry['HumanReadable'] = tableToMarkdown('ServiceNow records', records, removeNull=True) + entry['EntryContext'] = { + 'ServiceNow.Record(val.ID==obj.ID)': createContext(records) + } else: mapped_records = [{DEFAULT_RECORD_FIELDS[k]: r[k] for k in DEFAULT_RECORD_FIELDS} for r in result] entry['ReadableContentsFormat'] = formats['markdown'] - entry['HumanReadable'] = tableToMarkdown('ServiceNow query results', mapped_records, removeNull=True) + entry['HumanReadable'] = tableToMarkdown('ServiceNow records', mapped_records, removeNull=True) entry['EntryContext'] = { - 'ServiceNow.Results': createContext(mapped_records) + 'ServiceNow.Record(val.ID==obj.ID)': createContext(mapped_records) } return entry @@ -1035,7 +1066,7 @@ script: mapped_users = [{ 'ID': user['sys_id'], - 'Name': '{} {} {}'.format(user['first_name'], user['middle_name'], user['last_name']), + 'Name': '{} {}'.format(user['first_name'], user['last_name']), 'UserName': user['user_name'], 'Email': user['email'], 'Created': user['sys_created_on'], @@ -1220,6 +1251,8 @@ script: demisto.results(query_tickets_command()) elif demisto.command() == 'servicenow-upload-file' or demisto.command() == 'servicenow-incident-upload-file': demisto.results(upload_file_command()) + elif demisto.command() == 'servicenow-query-table': + demisto.results(query_table_command()) elif demisto.command() == 'servicenow-get-computer': demisto.results(get_computer_command()) elif demisto.command() == 'servicenow-query-computers': @@ -1243,7 +1276,6 @@ script: except Exception as e: LOG(e) LOG.print_log() - raise return_error(e.message) type: python commands: @@ -1267,41 +1299,45 @@ script: - name: get_attachments description: Whether to retrieve ticket attachments, default false outputs: - - contextPath: Ticket.ID - description: The unique ticket identifier. + - contextPath: ServiceNow.Ticket.ID + description: ServiceNow ticket ID type: string - - contextPath: Ticket.Creator - description: A string field that indicates the user who created the ticket. + - contextPath: ServiceNow.Ticket.OpenedBy + description: ServiceNow ticket opener ID type: string - - contextPath: Ticket.CreatedOn - description: The date and time when the ticket was created. + - contextPath: ServiceNow.Ticket.CreatedOn + description: ServiceNow ticket creation date. type: date - - contextPath: Ticket.Assignee - description: Specifies the user assigned to complete the ticket. By default, - this field uses a reference qualifier to only display users with the itil - role. + - contextPath: ServiceNow.Ticket.Assignee + description: ServiceNow ticket assignee ID type: string - - contextPath: Ticket.State - description: Status of the ticket. + - contextPath: ServiceNow.Ticket.State + description: ServiceNow ticket state type: string - - contextPath: Ticket.Summary - description: A human-readable title for the record. + - contextPath: ServiceNow.Ticket.Summary + description: ServiceNow ticket short summary type: string - - contextPath: Ticket.Number - description: The display value of the ticket. + - contextPath: ServiceNow.Ticket.Number + description: ServiceNow ticket number type: string - - contextPath: Ticket.Active - description: Specifies whether work is still being done on a task or whether - the work for the task is complete. + - contextPath: ServiceNow.Ticket.Active + description: ServiceNow ticket active type: boolean - - contextPath: Ticket.AdditionalComments - description: Comments about the task record. - - contextPath: Ticket.Priority - description: Specifies how high a priority the ticket should be for the assignee. + - contextPath: ServiceNow.Ticket.AdditionalComments + description: ServiceNow ticket comments type: string - - contextPath: Ticket.OpenedAt - description: The date and time when the ticket was opened for the first time. + - contextPath: ServiceNow.Ticket.Priority + description: ServiceNow ticket priority + type: string + - contextPath: ServiceNow.Ticket.OpenedAt + description: ServiceNow ticket opening time type: date + - contextPath: ServiceNow.Ticket.ResolvedBy + description: ServiceNow ticket resolver ID + type: string + - contextPath: ServiceNow.Ticket.CloseCode + description: ServiceNow ticket close code + type: string - contextPath: File.Info description: Attachment file info type: string @@ -1349,7 +1385,7 @@ script: description: Whether to retrieve ticket attachments, default false outputs: - contextPath: Ticket.ID - description: The unique ticket identifier. + description: The unique ticket identifier(sys_id). type: string - contextPath: Ticket.Creator description: A string field that indicates the user who created the ticket. @@ -1616,7 +1652,7 @@ script: - name: work_start description: Date when started to work on the ticket - name: assignment_group - description: Set AssignmentGroup - uuid of group like 46b87022a9fe198101a78787e40d7547 + description: Set AssignmentGroup - sys_id of group - name: incident_state description: integer - name: number @@ -1629,7 +1665,7 @@ script: - 3 - Moderate - 2 - High - 1 - Critical - description: Priority of the ticket (number) + description: Priority of the ticket - name: template description: Template name to use as a base to create new tickets. - name: custom_fields @@ -1645,24 +1681,45 @@ script: - name: state description: State of the ticket outputs: - - contextPath: Ticket.ID - description: ServiceNow ticket System ID + - contextPath: ServiceNow.Ticket.ID + description: ServiceNow ticket ID type: string - - contextPath: Ticket.Creator - description: ServiceNow ticket creator + - contextPath: ServiceNow.Ticket.OpenedBy + description: ServiceNow ticket opener ID type: string - - contextPath: Ticket.Assignee - description: ServiceNow ticket assignee + - contextPath: ServiceNow.Ticket.CreatedOn + description: ServiceNow ticket creation date. + type: date + - contextPath: ServiceNow.Ticket.Assignee + description: ServiceNow ticket assignee ID type: string - - contextPath: Ticket.State + - contextPath: ServiceNow.Ticket.State description: ServiceNow ticket state type: string - - contextPath: Ticket.Summary + - contextPath: ServiceNow.Ticket.Summary description: ServiceNow ticket short summary type: string - - contextPath: Ticket.Number + - contextPath: ServiceNow.Ticket.Number description: ServiceNow ticket number type: string + - contextPath: ServiceNow.Ticket.Active + description: ServiceNow ticket active + type: boolean + - contextPath: ServiceNow.Ticket.AdditionalComments + description: ServiceNow ticket comments + type: string + - contextPath: ServiceNow.Ticket.Priority + description: ServiceNow ticket priority + type: string + - contextPath: ServiceNow.Ticket.OpenedAt + description: ServiceNow ticket opening time + type: date + - contextPath: ServiceNow.Ticket.ResolvedBy + description: ServiceNow ticket resolver ID + type: string + - contextPath: ServiceNow.Ticket.CloseCode + description: ServiceNow ticket close code + type: string description: Create new ServiceNow ticket - name: servicenow-create deprecated: true @@ -2099,7 +2156,7 @@ script: - name: approval_history description: Ticket history approval - name: approval_set - description: Set ticket ApprovalSet - format "2016-07-02 21:51:11" glide_date_time + description: Set ticket ApprovalSet - format "2016-07-02 21:51:11" - name: assigned_to description: To whom the ticket is assigned - name: business_duration @@ -2241,7 +2298,7 @@ script: description: System ID of the ticket to update - name: custom_fields description: 'Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;...' - - name: type + - name: change_type auto: PREDEFINED predefined: - normal @@ -2909,6 +2966,7 @@ script: type: string description: Upload new file - name: servicenow-get-groups + deprecated: true arguments: - name: name required: true @@ -2922,6 +2980,7 @@ script: type: string description: Return information about specific servicenow group - name: servicenow-get-computer + deprecated: true arguments: - name: computerName required: true @@ -2949,7 +3008,8 @@ script: required: true description: Record system ID - name: fields - description: Table fields to display and output to the context + description: Comma separated table fields to display and output to the context, + e.g name,tag,company. ID field is added by default. - name: table_name required: true description: The name of the table to get the record from @@ -2984,7 +3044,8 @@ script: description: The query to run. For more information about querying in ServiceNow, see https://docs.servicenow.com/bundle/istanbul-servicenow-platform/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html - name: fields - description: Table fields to display and output to the context + description: Comma separated table fields to display and output to the context, + e.g name,tag,company. ID field is added by default. isArray: true outputs: - contextPath: ServiceNow.Results.ID @@ -3198,4 +3259,4 @@ script: description: Query the sys_user table in ServiceNow isfetch: true runonce: false -releaseNotes: "" \ No newline at end of file +releaseNotes: "Added ability to retrieve records from any table generically in addition to tickets. Added commands to query computers, groups and users. Also output improvements." \ No newline at end of file From ac30e3734bb0d82c579c530562eb124514d32357 Mon Sep 17 00:00:00 2001 From: Lior Blob Date: Tue, 27 Nov 2018 13:38:59 +0200 Subject: [PATCH 07/11] fixes #2, offset, rn, test playbooks --- Integrations/integration-ServiceNow.yml | 164 +- .../playbook-ServiceNow_Test.yml | 45 +- .../playbook-ServiceNow_Test_New.yml | 1743 +++++++++++++++++ 3 files changed, 1884 insertions(+), 68 deletions(-) create mode 100644 TestPlaybooks/NonCircleTests/playbook-ServiceNow_Test_New.yml diff --git a/Integrations/integration-ServiceNow.yml b/Integrations/integration-ServiceNow.yml index 0dc551533140..421ec7171c6d 100644 --- a/Integrations/integration-ServiceNow.yml +++ b/Integrations/integration-ServiceNow.yml @@ -156,6 +156,7 @@ script: DEFAULTS = { 'limit':10, + 'offset':0, 'fetch_limit':10 } @@ -243,11 +244,11 @@ script: } if 'closed_by' in data: - context['ResolvedBy'] = data["closed_by"]['value'] if 'value' in data['closed_by'] else '' + context['ResolvedBy'] = data['closed_by']['value'] if 'value' in data['closed_by'] else '' if 'opened_by' in data: - context['OpenedBy'] = data["opened_by"]['value'] if 'value' in data['opened_by'] else '' + context['OpenedBy'] = data['opened_by']['value'] if 'value' in data['opened_by'] else '' if 'assigned_to' in data: - context['Assignee'] = data["assigned_to"]['value'] if 'value' in data['assigned_to'] else '' + context['Assignee'] = data['assigned_to']['value'] if 'value' in data['assigned_to'] else '' if 'priority' in data: context['Priority'] = TICKET_PRIORITY.get(data['priority'], data['priority']) if 'state' in data: @@ -285,7 +286,7 @@ script: 'Description': ticket.get('description'), 'Opened at': ticket.get('opened_at'), 'Due date': ticket.get('due_date'), - 'Resolved by': ticket.get('closed_by', {}).get('value'), + 'Resolved by': ticket.get('closed_by', {}).get('value') if isinstance(ticket['closed_by'], dict) else ticket['closed_by'], 'Resolved at': ticket.get('resolved_at'), 'SLA due': ticket.get('sla_due'), 'Short description': ticket.get('short_description'), @@ -424,8 +425,8 @@ script: 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow ticket', hr, headers=headers, removeNull=True), 'EntryContext': { - 'Ticket(val.ID==obj.ID)': context, - 'ServiceNow.Ticket(val.ID==obj.ID)': context + 'Ticket(val.ID===obj.ID)': context, + 'ServiceNow.Ticket(val.ID===obj.ID)': context } } @@ -468,14 +469,14 @@ script: entry['ReadableContentsFormat'] = formats['markdown'] entry['HumanReadable'] = tableToMarkdown('ServiceNow record', record, removeNull=True) entry['EntryContext'] = { - 'ServiceNow.Record(val.ID==obj.ID)': createContext(record) + 'ServiceNow.Record(val.ID===obj.ID)': createContext(record) } else: mapped_record = {DEFAULT_RECORD_FIELDS[k]: record[k] for k in DEFAULT_RECORD_FIELDS} entry['ReadableContentsFormat'] = formats['markdown'] entry['HumanReadable'] = tableToMarkdown('ServiceNow record' + record_id, mapped_record, removeNull=True) entry['EntryContext'] = { - 'ServiceNow.Record(val.ID==obj.ID)': createContext(mapped_record) + 'ServiceNow.Record(val.ID===obj.ID)': createContext(mapped_record) } return entry @@ -570,7 +571,7 @@ script: 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow updated record', mapped_record, removeNull=True), 'EntryContext': { - 'ServiceNow.Record(val.ID==obj.ID)': createContext(mapped_record) + 'ServiceNow.Record(val.ID===obj.ID)': createContext(mapped_record) } } @@ -597,7 +598,7 @@ script: res = create(ticket_type, fields, custom_fields, template) hr = get_ticket_human_readable(res['result'], ticket_type) - context = get_ticket_context(ticket, ticket_type) + context = get_ticket_context(res['result'], ticket_type) headers = ['System ID','Number','Impact','Urgency','Severity','Priority','State','Created On','Created By','Active','Close notes','Close code', 'Description','Opened at','Due date','Resolved by','Resolved at','SLA due','Short description','Additional Comments'] @@ -609,8 +610,8 @@ script: 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow ticket created successfully', hr, headers=headers, removeNull=True), 'EntryContext': { - 'Ticket(val.ID==obj.ID)': context, - 'ServiceNow.Ticket(val.ID==obj.ID)': context + 'Ticket(val.ID===obj.ID)': context, + 'ServiceNow.Ticket(val.ID===obj.ID)': context } } @@ -642,7 +643,7 @@ script: 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow created record', mapped_record, removeNull=True), 'EntryContext': { - 'ServiceNow.Record(val.ID==obj.ID)': createContext(mapped_record) + 'ServiceNow.Record(val.ID===obj.ID)': createContext(mapped_record) } } @@ -666,7 +667,7 @@ script: 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['text'], - 'HumanReadable': 'Ticket with ID ' + ticket_id + ' was succesfully deleted.' + 'HumanReadable': 'Ticket with ID ' + ticket_id + ' was successfully deleted.' } return entry @@ -682,7 +683,7 @@ script: 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['text'], - 'HumanReadable': 'Record with ID ' + record_id + ' was succesfully deleted.' + 'HumanReadable': 'Record with ID ' + record_id + ' was successfully deleted.' } return entry @@ -751,12 +752,14 @@ script: def query_tickets_command(): sysparm_limit = demisto.args().get('limit', DEFAULTS['limit']) sysparm_query = demisto.args().get('query') + sysparm_offset = demisto.args().get('offset', DEFAULTS['offset']) + if not sysparm_query: #backward compatibility sysparm_query = demisto.args().get('sysparm_query') ticket_type = get_table_name(demisto.args().get('ticket_type')) - res = query(ticket_type, sysparm_limit, sysparm_query) + res = query(ticket_type, sysparm_limit, sysparm_offset, sysparm_query) if not res or 'result' not in res or len(res['result']) == 0: return 'No results found' @@ -774,8 +777,8 @@ script: 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow tickets', hr, headers=headers, removeNull=True), 'EntryContext': { - 'Ticket(val.ID==obj.ID)': context, - 'ServiceNow.Ticket(val.ID==obj.ID)': context + 'Ticket(val.ID===obj.ID)': context, + 'ServiceNow.Ticket(val.ID===obj.ID)': context } } @@ -786,9 +789,10 @@ script: table_name = demisto.args()['table_name'] sysparm_limit = demisto.args().get('limit', DEFAULTS['limit']) sysparm_query = demisto.args().get('query') + sysparm_offset = demisto.args().get('offset', DEFAULTS['offset']) fields = demisto.args().get('fields') - res = query(table_name, sysparm_limit, sysparm_query) + res = query(table_name, sysparm_limit, sysparm_offset, sysparm_query) if not res or 'result' not in res or len(res['result']) == 0: return 'No results found' @@ -814,22 +818,23 @@ script: entry['ReadableContentsFormat'] = formats['markdown'] entry['HumanReadable'] = tableToMarkdown('ServiceNow records', records, removeNull=True) entry['EntryContext'] = { - 'ServiceNow.Record(val.ID==obj.ID)': createContext(records) + 'ServiceNow.Record(val.ID===obj.ID)': createContext(records) } else: mapped_records = [{DEFAULT_RECORD_FIELDS[k]: r[k] for k in DEFAULT_RECORD_FIELDS} for r in result] entry['ReadableContentsFormat'] = formats['markdown'] entry['HumanReadable'] = tableToMarkdown('ServiceNow records', mapped_records, removeNull=True) entry['EntryContext'] = { - 'ServiceNow.Record(val.ID==obj.ID)': createContext(mapped_records) + 'ServiceNow.Record(val.ID===obj.ID)': createContext(mapped_records) } return entry - def query(table_name, sysparm_limit, sysparm_query): + def query(table_name, sysparm_limit, sysparm_offset, sysparm_query): query_params = {} query_params['sysparm_limit'] = sysparm_limit + query_params['sysparm_offset'] = sysparm_offset if sysparm_query: query_params['sysparm_query'] = sysparm_query @@ -869,7 +874,7 @@ script: 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('File Uploaded', hr), 'EntryContext': { - 'Ticket(val.ID==obj.ID)' : context + 'Ticket(val.ID===obj.ID)' : context } } @@ -942,7 +947,8 @@ script: computer_name = demisto.args().get('computer_name') asset_tag = demisto.args().get('asset_tag') computer_query = demisto.args().get('query', {}) - limit = demisto.args().get('limit') + offset = demisto.args().get('offset', DEFAULTS['offset']) + limit = demisto.args().get('limit', DEFAULTS['limit']) if computer_id: res = get(table_name, computer_id) @@ -952,17 +958,19 @@ script: elif asset_tag: computer_query = 'asset_tag=' + asset_tag - res = query(table_name, limit, computer_query) + res = query(table_name, limit, offset, computer_query) if not res or 'result' not in res: - return 'Cannot find computers' + return 'No computers found' computers = res['result'] if not isinstance(computers, list): computers = [computers] if len(computers) == 0: - return 'Cannot find computers' + return 'No computers found' + + headers = ['ID','AssetTag','Name','DisplayName','SupportGroup','OperatingSystem','Company','AssignedTo','State','Cost','Comments'] mapped_computers = [{ 'ID': computer['sys_id'], @@ -972,7 +980,7 @@ script: 'SupportGroup': computer['support_group'], 'OperatingSystem': computer['os'], 'Company': computer['company'], - 'AssignedTo': computer['assigned_to'], + 'AssignedTo': computer.get('assigned_to', {}).get('value') if isinstance(computer['assigned_to'], dict) else computer['assigned_to'], 'State': COMPUTER_STATUS.get(computer['install_status'], computer['install_status']), 'Cost': '{} {}'.format(computer['cost'], computer['cost_cc']), 'Comments': computer['comments'] @@ -983,46 +991,48 @@ script: 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('ServiceNow Computer', mapped_computers, removeNull=True), + 'HumanReadable': tableToMarkdown('ServiceNow Computers', mapped_computers, headers=headers, removeNull=True), 'EntryContext': { - 'ServiceNow.Computer(val.ID==obj.ID)': createContext(mapped_computers, removeNull=True), + 'ServiceNow.Computer(val.ID===obj.ID)': createContext(mapped_computers, removeNull=True), } } return entry - def query_groups_command(): table_name = 'sys_user_group' group_id = demisto.args().get('group_id') group_name = demisto.args().get('group_name') group_query = demisto.args().get('query', {}) - limit = demisto.args().get('limit') + offset = demisto.args().get('offset', DEFAULTS['offset']) + limit = demisto.args().get('limit', DEFAULTS['limit']) if group_id: res = get(table_name, group_id) else: if group_name: group_query = 'name=' + group_name - res = query(table_name, limit, group_query) + res = query(table_name, limit, offset, group_query) if not res or 'result' not in res: - return 'Cannot find groups' + return 'No groups found' groups = res['result'] if not isinstance(groups, list): groups = [groups] if len(groups) == 0: - return 'Cannot find groups' + return 'No groups found' + + headers = ['ID','Description','Name','Active','Manager','Updated'] mapped_groups = [{ 'ID': group['sys_id'], 'Description': group['description'], 'Name': group['name'], 'Active': group['active'], - 'Manager': group['manager'], + 'Manager': group.get('manager', {}).get('value') if isinstance(group['manager'], dict) else group['manager'], 'Updated': group['sys_updated_on'], } for group in groups] @@ -1031,9 +1041,9 @@ script: 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('ServiceNow Groups', mapped_groups, removeNull=True), + 'HumanReadable': tableToMarkdown('ServiceNow Groups', mapped_groups, headers=headers, removeNull=True), 'EntryContext': { - 'ServiceNow.Group(val.ID==obj.ID)': createContext(mapped_groups, removeNull=True), + 'ServiceNow.Group(val.ID===obj.ID)': createContext(mapped_groups, removeNull=True), } } @@ -1045,24 +1055,27 @@ script: user_id = demisto.args().get('user_id') user_name = demisto.args().get('user_name') user_query = demisto.args().get('query', {}) - limit = demisto.args().get('limit') + offset = demisto.args().get('offset', DEFAULTS['offset']) + limit = demisto.args().get('limit', DEFAULTS['limit']) if user_id: res = get(table_name, user_id) else: if user_name: user_query = 'user_name=' + user_name - res = query(table_name, limit, user_query) + res = query(table_name, limit, offset, user_query) if not res or 'result' not in res: - return 'Cannot find users' + return 'No users found' users = res['result'] if not isinstance(users, list): users = [users] if len(users) == 0: - return 'Cannot find users' + return 'No users found' + + headers = ['ID','Name','UserName','Email','Created','Updated'] mapped_users = [{ 'ID': user['sys_id'], @@ -1078,9 +1091,9 @@ script: 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('ServiceNow Users', mapped_users, removeNull=True), + 'HumanReadable': tableToMarkdown('ServiceNow Users', mapped_users, headers=headers, removeNull=True), 'EntryContext': { - 'ServiceNow.User(val.ID==obj.ID)': createContext(mapped_users, removeNull=True), + 'ServiceNow.User(val.ID===obj.ID)': createContext(mapped_users, removeNull=True), } } @@ -1093,7 +1106,7 @@ script: res = query(table_name, None, 'name=' + group_name) if not res or 'result' not in res: - return 'Cannot find groups' + return 'No groups found' hr_groups = [] context_groups = [] @@ -1226,6 +1239,8 @@ script: demisto.incidents(incidents) demisto.setLastRun({'time': snow_time}) + LOG('Executing command ' + demisto.command()) + try: if demisto.command() == 'test-module': path = "table/incident?sysparm_limit=1" @@ -1480,7 +1495,8 @@ script: - contextPath: Ticket.Number description: ServiceNow ticket number type: string - description: Retrieve ticket information by specific ticket ID + description: Deprecated. Please use servicenow-get-ticket or servicenow-get-record + instead - name: servicenow-create-ticket arguments: - name: short_description @@ -1523,13 +1539,13 @@ script: - "false" description: Set ticket as Active - name: activity_due - description: Set ticket ActivityDue - format "2016-07-02 21:51:11" glide_date_time + description: Set ticket ActivityDue - format "2016-07-02 21:51:11" - name: additional_assignee_list description: List of assigned users to the ticket - name: approval_history description: Ticket history approval - name: approval_set - description: Set ticket ApprovalSet - format "2016-07-02 21:51:11" glide_date_time + description: Set ticket ApprovalSet - format "2016-07-02 21:51:11" - name: assigned_to description: To whom the ticket is assigned - name: business_duration @@ -1670,7 +1686,7 @@ script: description: Template name to use as a base to create new tickets. - name: custom_fields description: 'Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;...' - - name: type + - name: change_type auto: PREDEFINED predefined: - normal @@ -2107,7 +2123,7 @@ script: - contextPath: Ticket.Number description: ServiceNow ticket number type: string - description: Create new ServiceNow ticket + description: Create a new ServiceNow ticket - name: servicenow-update-ticket arguments: - name: short_description @@ -2308,7 +2324,7 @@ script: defaultValue: normal - name: state description: State of the ticket - description: Update specific ticket by providing ticket id + description: Update specific ticket - name: servicenow-update deprecated: true arguments: @@ -2659,7 +2675,8 @@ script: - name: id required: true description: System ID of the ticket to update - description: Update specific ticket by providing ticket id + description: Deprecated. Please use servicenow-update-ticket or servicenow-update-record + instead - name: servicenow-delete-ticket arguments: - name: id @@ -2699,7 +2716,7 @@ script: the link as WorkNote, format bool - name: text description: The text to represent the link - description: Add link to specific ticket by providing ticket id + description: Add a link to specific ticket - name: servicenow-incident-add-link deprecated: true arguments: @@ -2723,7 +2740,7 @@ script: false will publish the link as WorkNote, format bool - name: text description: The text to represent the link - description: Add link to specific ticket by providing ticket id + description: Deprecated. Please use servicenow-add-link instead. - name: servicenow-add-comment arguments: - name: id @@ -2766,7 +2783,7 @@ script: - name: post-as-comment description: Publish the link as comment on the ticket, if false will publish the link as WorkNote, format bool - description: Add comment to specific ticket by providing ticket id + description: Deprecated. Please use servicenow-add-comment instead. - name: servicenow-query-tickets arguments: - name: limit @@ -2784,6 +2801,9 @@ script: defaultValue: incident - name: query description: The query to run. To learn about querying in ServiceNow, see https://docs.servicenow.com/bundle/istanbul-servicenow-platform/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html + - name: offset + description: Starting record index to begin retrieving records from + defaultValue: "0" outputs: - contextPath: Ticket.ID description: The unique ticket identifier. @@ -2823,7 +2843,7 @@ script: - contextPath: Ticket.Escalation description: Indicates how long the ticket has been open. type: string - description: Retrieve ticket info with query + description: Retrieve ticket info with a query - name: servicenow-query deprecated: true arguments: @@ -2933,7 +2953,7 @@ script: - contextPath: Ticket.File.SystemID description: System ID of the file type: string - description: Upload new file + description: Upload a file to a specific ticket - name: servicenow-incident-upload-file deprecated: true arguments: @@ -2964,9 +2984,8 @@ script: - contextPath: Ticket.File.SystemID description: System ID of the file type: string - description: Upload new file + description: Deprecated. Please use servicenow-upload-file instead. - name: servicenow-get-groups - deprecated: true arguments: - name: name required: true @@ -2978,9 +2997,8 @@ script: - contextPath: ServiceNowGroups.GroupName description: Group name type: string - description: Return information about specific servicenow group + description: Deprecated. Please use servicenow-query-groups instead. - name: servicenow-get-computer - deprecated: true arguments: - name: computerName required: true @@ -3001,7 +3019,7 @@ script: - contextPath: ServiceNowComputer.comments description: Comments type: string - description: query the cmdb_ci_computer table with a computer code + description: Deprecated. Please user servicenow-query-computers instead. - name: servicenow-get-record arguments: - name: id @@ -3047,6 +3065,9 @@ script: description: Comma separated table fields to display and output to the context, e.g name,tag,company. ID field is added by default. isArray: true + - name: offset + description: Starting record index to begin retrieving records from + defaultValue: "0" outputs: - contextPath: ServiceNow.Results.ID description: The unique record identifier for the record. @@ -3163,6 +3184,9 @@ script: - name: limit description: Query results limit defaultValue: "10" + - name: offset + description: Starting record index to begin retrieving records from + defaultValue: "0" outputs: - contextPath: ServiceNow.Computer.ID description: Computer sys_id @@ -3209,6 +3233,10 @@ script: ServiceNow, see https://docs.servicenow.com/bundle/istanbul-servicenow-platform/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html - name: limit description: Query results limit + defaultValue: "10" + - name: offset + description: Starting record index to begin retrieving records from + defaultValue: "0" outputs: - contextPath: ServiceNow.Group.ID description: Group sys_id @@ -3237,6 +3265,10 @@ script: ServiceNow, see https://docs.servicenow.com/bundle/istanbul-servicenow-platform/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html - name: limit description: Query results limit + defaultValue: "10" + - name: offset + description: Starting record index to begin retrieving records from + defaultValue: "0" outputs: - contextPath: ServiceNow.User.ID description: User sys_id @@ -3259,4 +3291,10 @@ script: description: Query the sys_user table in ServiceNow isfetch: true runonce: false -releaseNotes: "Added ability to retrieve records from any table generically in addition to tickets. Added commands to query computers, groups and users. Also output improvements." \ No newline at end of file +releaseNotes: "Added ability to retrieve records from any table generically in addition to tickets. + the commands are separated in the following way: + servicenow-get -> servicenow-get-ticket, servicenow-get-record + servicenow-create -> servicenow-create-ticket, servicenow-create-record + servicenow-update -> servicenow-update-ticket, servicenow-update-record, + servicenow-query -> servicenow-query-tickets, servicenow-query-table + Added commands to query computers, groups and users. Also output improvements." \ No newline at end of file diff --git a/TestPlaybooks/NonCircleTests/playbook-ServiceNow_Test.yml b/TestPlaybooks/NonCircleTests/playbook-ServiceNow_Test.yml index 0d223c602ef1..d78171ee3bc8 100644 --- a/TestPlaybooks/NonCircleTests/playbook-ServiceNow_Test.yml +++ b/TestPlaybooks/NonCircleTests/playbook-ServiceNow_Test.yml @@ -1,5 +1,5 @@ id: servicenow_test -version: 20 +version: -1 name: ServiceNow Test description: This playbook tests the ServiceNow integration commands. starttaskid: "0" @@ -26,6 +26,7 @@ tasks: } } note: false + timertriggers: [] "1": id: "1" taskid: 11063b19-9c0f-4515-8059-b4b497c968b2 @@ -55,6 +56,7 @@ tasks: } } note: false + timertriggers: [] "2": id: "2" taskid: af8bf494-e593-4f57-86fe-93de46f91ad8 @@ -152,6 +154,7 @@ tasks: } } note: false + timertriggers: [] "6": id: "6" taskid: b5e47340-57f7-4197-803e-da438c182caa @@ -191,6 +194,7 @@ tasks: } } note: false + timertriggers: [] "7": id: "7" taskid: 2e397644-2c39-4620-8b20-6021af54838f @@ -288,6 +292,7 @@ tasks: } } note: false + timertriggers: [] "8": id: "8" taskid: a826bc45-36b0-402d-8dea-a369177ee1d3 @@ -327,6 +332,7 @@ tasks: } } note: false + timertriggers: [] "15": id: "15" taskid: d2b17f13-7fbb-4591-8dcf-0722dbd5c8c1 @@ -350,6 +356,7 @@ tasks: } } note: false + timertriggers: [] "18": id: "18" taskid: 1f1dcd0b-d837-475e-8016-01e116314957 @@ -373,6 +380,7 @@ tasks: } } note: false + timertriggers: [] "24": id: "24" taskid: 41382c39-cb5e-43c7-8748-d509be51c63d @@ -402,6 +410,7 @@ tasks: } } note: false + timertriggers: [] "25": id: "25" taskid: 1968f536-0a02-4fdc-8880-518d720ee139 @@ -431,6 +440,7 @@ tasks: } } note: false + timertriggers: [] "26": id: "26" taskid: c9c7c8fa-e24c-4772-83f1-54e09105c562 @@ -528,6 +538,7 @@ tasks: } } note: false + timertriggers: [] "28": id: "28" taskid: 990af179-91e5-438d-8976-1b126da46be0 @@ -558,6 +569,7 @@ tasks: } } note: false + timertriggers: [] "29": id: "29" taskid: 13f3c178-59be-42a5-8cac-a3f943e074d1 @@ -597,6 +609,7 @@ tasks: } } note: false + timertriggers: [] "30": id: "30" taskid: 581c4f00-74a8-4f5f-8760-d377605b6221 @@ -628,6 +641,7 @@ tasks: } } note: false + timertriggers: [] "31": id: "31" taskid: 9c3d92e3-6ac0-4fde-8844-d8aeaf41af7d @@ -660,6 +674,7 @@ tasks: } } note: false + timertriggers: [] "33": id: "33" taskid: 54c7fb27-4836-4f5e-8c7c-7adb523b031a @@ -690,6 +705,7 @@ tasks: } } note: false + timertriggers: [] "35": id: "35" taskid: 8c055dee-25f1-4f73-8fb3-bf3fa04d7078 @@ -719,6 +735,7 @@ tasks: } } note: false + timertriggers: [] "36": id: "36" taskid: 795072c5-5b27-4d2a-83af-640e4d8147d0 @@ -749,6 +766,7 @@ tasks: } } note: false + timertriggers: [] "37": id: "37" taskid: c75fc8dd-fe88-43b1-8926-2c98fd6743bb @@ -778,6 +796,7 @@ tasks: } } note: false + timertriggers: [] "40": id: "40" taskid: d2ddf2d4-a568-45cb-84ce-0a6939e57ba3 @@ -807,6 +826,7 @@ tasks: } } note: false + timertriggers: [] "41": id: "41" taskid: 891181f3-e216-4d70-87b9-c1f3815ab22b @@ -836,6 +856,7 @@ tasks: } } note: false + timertriggers: [] "42": id: "42" taskid: 574fd6e5-c63a-4e80-8ee0-86699dd4ac28 @@ -865,6 +886,7 @@ tasks: } } note: false + timertriggers: [] "43": id: "43" taskid: 33bea087-a196-476e-84c5-34eca5a30aa8 @@ -894,6 +916,7 @@ tasks: } } note: false + timertriggers: [] "44": id: "44" taskid: 1881c0da-1a5e-478b-8420-4a259c3457eb @@ -923,6 +946,7 @@ tasks: } } note: false + timertriggers: [] "45": id: "45" taskid: a40ccffd-feda-4d50-80ed-462e9e3844fc @@ -954,6 +978,7 @@ tasks: } } note: false + timertriggers: [] "46": id: "46" taskid: 18460c88-cefb-42c5-829e-b7ae3b067072 @@ -991,6 +1016,7 @@ tasks: } } note: false + timertriggers: [] "47": id: "47" taskid: 771f1530-11aa-4f19-82aa-be984bf9567e @@ -1011,6 +1037,7 @@ tasks: } } note: false + timertriggers: [] "48": id: "48" taskid: 24703920-6f3c-4308-8d13-4446956af85b @@ -1039,6 +1066,7 @@ tasks: } } note: false + timertriggers: [] "49": id: "49" taskid: 8dcec7a9-9c6a-40ed-801d-d4df1d4192b1 @@ -1074,6 +1102,7 @@ tasks: } } note: false + timertriggers: [] "50": id: "50" taskid: 7217c824-3a59-47b9-8708-db0d7e8db551 @@ -1106,6 +1135,7 @@ tasks: } } note: false + timertriggers: [] "51": id: "51" taskid: 7a195718-e882-4017-8e01-3cfcb9f07276 @@ -1141,6 +1171,7 @@ tasks: } } note: false + timertriggers: [] "52": id: "52" taskid: 825f7a30-af72-423b-89ff-fc1956af5045 @@ -1173,6 +1204,7 @@ tasks: } } note: false + timertriggers: [] "53": id: "53" taskid: 33e33025-0b89-4e41-8291-0ca44a6e4f90 @@ -1206,12 +1238,13 @@ tasks: } } note: false + timertriggers: [] "54": id: "54" - taskid: c3aa8dc0-adac-497a-830a-8d511a0c5491 + taskid: 7ab6f886-dad3-4776-8972-53aacec27312 type: regular task: - id: c3aa8dc0-adac-497a-830a-8d511a0c5491 + id: 7ab6f886-dad3-4776-8972-53aacec27312 version: -1 name: VerifyContext description: |- @@ -1230,9 +1263,9 @@ tasks: scriptarguments: expectedValue: {} fields: - simple: sys_id,state,active + simple: ID,state,active path: - simple: ServiceNow.Results + simple: ServiceNow.Record separatecontext: false view: |- { @@ -1242,6 +1275,7 @@ tasks: } } note: false + timertriggers: [] "55": id: "55" taskid: 18d47a02-871b-45af-898e-c11b2af5cfcc @@ -1271,6 +1305,7 @@ tasks: } } note: false + timertriggers: [] view: |- { "linkLabelsPosition": {}, diff --git a/TestPlaybooks/NonCircleTests/playbook-ServiceNow_Test_New.yml b/TestPlaybooks/NonCircleTests/playbook-ServiceNow_Test_New.yml new file mode 100644 index 000000000000..74d75e9d4c8b --- /dev/null +++ b/TestPlaybooks/NonCircleTests/playbook-ServiceNow_Test_New.yml @@ -0,0 +1,1743 @@ +id: servicenow_test_new +version: -1 +name: ServiceNow Test New +description: This playbook tests the ServiceNow integration commands. +starttaskid: "0" +tasks: + "0": + id: "0" + taskid: 67dc347d-93a3-4375-8eed-b618cb1f3f86 + type: start + task: + id: 67dc347d-93a3-4375-8eed-b618cb1f3f86 + version: -1 + name: "" + iscommand: false + brand: "" + nexttasks: + '#none#': + - "1" + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 50 + } + } + note: false + timertriggers: [] + "1": + id: "1" + taskid: 11063b19-9c0f-4515-8059-b4b497c968b2 + type: regular + task: + id: 11063b19-9c0f-4515-8059-b4b497c968b2 + version: -1 + name: DeleteContext (all) + description: Delete field from context + scriptName: DeleteContext + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "15" + scriptarguments: + all: + simple: "yes" + key: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 195 + } + } + note: false + timertriggers: [] + "2": + id: "2" + taskid: 9a303255-732d-4ea3-8e1f-7dfbffffac5b + type: regular + task: + id: 9a303255-732d-4ea3-8e1f-7dfbffffac5b + version: -1 + name: 'servicenow-create-ticket, Summary: "Hello World"' + description: Create new ServiceNow ticket + script: ServiceNow|||servicenow-create-ticket + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "6" + - "35" + scriptarguments: + active: {} + activity_due: {} + additional_assignee_list: {} + approval_history: {} + approval_set: {} + assigned_to: + simple: Johnny Doe + assignment_group: {} + business_duration: {} + business_service: {} + business_stc: {} + calendar_duration: {} + caller_id: {} + category: {} + caused_by: {} + change_type: {} + close_code: {} + close_notes: {} + closed_at: {} + closed_by: {} + cmdb_ci: {} + comments: {} + comments_and_work_notes: {} + company: {} + contact_type: {} + correlation_display: {} + correlation_id: {} + custom_fields: {} + delivery_plan: {} + description: {} + display: {} + due_date: {} + escalation: {} + expected_start: {} + follow_up: {} + group_list: {} + impact: {} + incident_state: {} + knowledge: {} + location: {} + made_sla: {} + notify: {} + number: {} + order: {} + parent: {} + parent_incident: {} + priority: {} + problem_id: {} + reassignment_count: {} + reopen_count: {} + resolved_at: {} + resolved_by: {} + rfc: {} + severity: {} + short_description: + simple: Hello World + sla_due: {} + state: {} + subcategory: {} + sys_updated_by: {} + sys_updated_on: {} + template: {} + ticket_type: {} + urgency: {} + user_input: {} + watch_list: {} + work_end: {} + work_notes: {} + work_notes_list: {} + work_start: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 515 + } + } + note: false + timertriggers: [] + "6": + id: "6" + taskid: 24017728-cc30-4649-877a-d98e782d37a6 + type: regular + task: + id: 24017728-cc30-4649-877a-d98e782d37a6 + version: -1 + name: 'VerifyContextFields: Summary, Assignee' + scriptName: VerifyContextFields + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "7" + scriptarguments: + field1: + simple: ServiceNow.Ticket.Assignee + field2: + simple: ServiceNow.Ticket.Summary + field3: {} + field4: {} + field5: {} + value1: + simple: Johnny Doe + value2: + simple: Hello World + value3: {} + value4: {} + value5: {} + separatecontext: false + view: |- + { + "position": { + "x": 50, + "y": 690 + } + } + note: false + timertriggers: [] + "7": + id: "7" + taskid: 55b97abb-5187-44c0-89b8-6788bd1b645e + type: regular + task: + id: 55b97abb-5187-44c0-89b8-6788bd1b645e + version: -1 + name: 'servicenow-create-ticket, Summary: "Lovely Day"' + description: Create new ServiceNow ticket + script: ServiceNow|||servicenow-create-ticket + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "8" + - "36" + scriptarguments: + active: {} + activity_due: {} + additional_assignee_list: {} + approval_history: {} + approval_set: {} + assigned_to: + simple: Lucy Liu + assignment_group: {} + business_duration: {} + business_service: {} + business_stc: {} + calendar_duration: {} + caller_id: {} + category: {} + caused_by: {} + change_type: {} + close_code: {} + close_notes: {} + closed_at: {} + closed_by: {} + cmdb_ci: {} + comments: {} + comments_and_work_notes: {} + company: {} + contact_type: {} + correlation_display: {} + correlation_id: {} + custom_fields: {} + delivery_plan: {} + description: {} + display: {} + due_date: {} + escalation: {} + expected_start: {} + follow_up: {} + group_list: {} + impact: {} + incident_state: {} + knowledge: {} + location: {} + made_sla: {} + notify: {} + number: {} + order: {} + parent: {} + parent_incident: {} + priority: {} + problem_id: {} + reassignment_count: {} + reopen_count: {} + resolved_at: {} + resolved_by: {} + rfc: {} + severity: {} + short_description: + simple: Lovely Day + sla_due: {} + state: {} + subcategory: {} + sys_updated_by: {} + sys_updated_on: {} + template: {} + ticket_type: {} + urgency: {} + user_input: {} + watch_list: {} + work_end: {} + work_notes: {} + work_notes_list: {} + work_start: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 865 + } + } + note: false + timertriggers: [] + "8": + id: "8" + taskid: 38935e25-02d0-4b88-8153-7de3224e6279 + type: regular + task: + id: 38935e25-02d0-4b88-8153-7de3224e6279 + version: -1 + name: 'VerifyContextFields: ServiceNow.Ticket[1].Summary, ServiceNow.Ticket[1].Assignee' + scriptName: VerifyContextFields + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "18" + scriptarguments: + field1: + simple: ServiceNow.Ticket.[1].Assignee + field2: + simple: ServiceNow.Ticket.[1].Summary + field3: {} + field4: {} + field5: {} + value1: + simple: Lucy Liu + value2: + simple: Lovely Day + value3: {} + value4: {} + value5: {} + separatecontext: false + view: |- + { + "position": { + "x": 50, + "y": 1040 + } + } + note: false + timertriggers: [] + "15": + id: "15" + taskid: d2b17f13-7fbb-4591-8dcf-0722dbd5c8c1 + type: title + task: + id: d2b17f13-7fbb-4591-8dcf-0722dbd5c8c1 + version: -1 + name: Create two tickets + type: title + iscommand: false + brand: "" + nexttasks: + '#none#': + - "2" + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 370 + } + } + note: false + timertriggers: [] + "18": + id: "18" + taskid: 1f1dcd0b-d837-475e-8016-01e116314957 + type: title + task: + id: 1f1dcd0b-d837-475e-8016-01e116314957 + version: -1 + name: Query and update tickets + type: title + iscommand: false + brand: "" + nexttasks: + '#none#': + - "24" + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 1215 + } + } + note: false + timertriggers: [] + "24": + id: "24" + taskid: 263ed649-f6db-4ea5-8397-a7b954b8fc70 + type: regular + task: + id: 263ed649-f6db-4ea5-8397-a7b954b8fc70 + version: -1 + name: 'servicenow-query-tickets Ticket #0' + script: ServiceNow|||servicenow-query-tickets + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "37" + scriptarguments: + limit: {} + query: + simple: sys_id=${Ticket.[0].ID} + ticket_type: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 1360 + } + } + note: false + timertriggers: [] + "25": + id: "25" + taskid: d5acf6cc-0c15-4e49-8aae-19f54bbd91ec + type: regular + task: + id: d5acf6cc-0c15-4e49-8aae-19f54bbd91ec + version: -1 + name: 'servicenow-get-ticket Ticket #1' + script: ServiceNow|||servicenow-get-ticket + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "41" + scriptarguments: + get_attachments: {} + id: + simple: ${Ticket.[1].ID} + number: {} + ticket_type: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 1710 + } + } + note: false + timertriggers: [] + "26": + id: "26" + taskid: 6651062d-f4a4-453e-8296-6745b38da44e + type: regular + task: + id: 6651062d-f4a4-453e-8296-6745b38da44e + version: -1 + name: 'servicenow-update-ticket Ticket #0, Summary: "Goodbye Please", Assignee: + "New Assignee"' + script: '|||servicenow-update-ticket' + type: regular + iscommand: true + brand: "" + nexttasks: + '#none#': + - "28" + scriptarguments: + active: {} + activity_due: {} + additional_assignee_list: {} + approval_history: {} + approval_set: {} + assigned_to: + simple: New Assignee + assignment_group: {} + business_duration: {} + business_service: {} + business_stc: {} + calendar_duration: {} + caller_id: {} + category: {} + caused_by: {} + change_type: {} + close_code: {} + close_notes: {} + closed_at: {} + closed_by: {} + cmdb_ci: {} + comments: {} + comments_and_work_notes: {} + company: {} + contact_type: {} + correlation_display: {} + correlation_id: {} + custom_fields: {} + delivery_plan: {} + description: {} + display: {} + due_date: {} + escalation: {} + expected_start: {} + follow_up: {} + group_list: {} + id: + simple: ${Ticket.[0].ID} + impact: {} + incident_state: {} + knowledge: {} + location: {} + made_sla: {} + notify: {} + number: {} + order: {} + parent: {} + parent_incident: {} + priority: {} + problem_id: {} + reassignment_count: {} + reopen_count: {} + resolved_at: {} + resolved_by: {} + rfc: {} + severity: {} + short_description: + simple: Goodbye Please + sla_due: {} + state: {} + subcategory: {} + sys_updated_by: {} + sys_updated_on: {} + ticket_type: {} + urgency: {} + user_input: {} + watch_list: {} + work_end: {} + work_notes: {} + work_notes_list: {} + work_start: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 2060 + } + } + note: false + timertriggers: [] + "28": + id: "28" + taskid: 7ed76685-7ef4-4101-8b2f-79cad7c40443 + type: regular + task: + id: 7ed76685-7ef4-4101-8b2f-79cad7c40443 + version: -1 + name: 'servicenow-query Ticket #0' + script: '|||servicenow-query-tickets' + type: regular + iscommand: true + brand: "" + nexttasks: + '#none#': + - "40" + - "29" + scriptarguments: + limit: {} + query: + simple: sys_id=${Ticket.[0].ID} + ticket_type: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 2235 + } + } + note: false + timertriggers: [] + "29": + id: "29" + taskid: 0290651c-95ac-453c-8ce2-8373e47a10a0 + type: regular + task: + id: 0290651c-95ac-453c-8ce2-8373e47a10a0 + version: -1 + name: 'VerifyContextFields (Entry updated - #0, ServiceNow.Ticket[0])' + scriptName: VerifyContextFields + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "30" + scriptarguments: + field1: + simple: ServiceNow.Ticket.[0].Summary + field2: + simple: ServiceNow.Ticket.[0].Assignee + field3: {} + field4: {} + field5: {} + value1: + simple: Goodbye Please + value2: + simple: New Assignee + value3: {} + value4: {} + value5: {} + separatecontext: false + view: |- + { + "position": { + "x": 50, + "y": 2410 + } + } + note: false + timertriggers: [] + "30": + id: "30" + taskid: 581c4f00-74a8-4f5f-8760-d377605b6221 + type: regular + task: + id: 581c4f00-74a8-4f5f-8760-d377605b6221 + version: -1 + name: 'servicenow-add-comment Ticket #0' + script: ServiceNow|||servicenow-add-comment + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "42" + scriptarguments: + comment: + simple: This is a new comment + id: + simple: ${Ticket.[0].ID} + post-as-comment: {} + ticket_type: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 2585 + } + } + note: false + timertriggers: [] + "31": + id: "31" + taskid: 9c3d92e3-6ac0-4fde-8844-d8aeaf41af7d + type: regular + task: + id: 9c3d92e3-6ac0-4fde-8844-d8aeaf41af7d + version: -1 + name: 'servicenow-add-link Ticket #0' + script: ServiceNow|||servicenow-add-link + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "43" + scriptarguments: + id: + simple: ${Ticket.[0].ID} + link: + simple: http://www.google.com + post-as-comment: {} + text: {} + ticket_type: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 2935 + } + } + note: false + timertriggers: [] + "33": + id: "33" + taskid: 54c7fb27-4836-4f5e-8c7c-7adb523b031a + type: regular + task: + id: 54c7fb27-4836-4f5e-8c7c-7adb523b031a + version: -1 + name: Upload File to War Room + scriptName: FileCreateAndUpload + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "46" + scriptarguments: + data: + simple: file data + entryId: {} + filename: + simple: test_file.txt + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 3285 + } + } + note: false + timertriggers: [] + "35": + id: "35" + taskid: 8c055dee-25f1-4f73-8fb3-bf3fa04d7078 + type: regular + task: + id: 8c055dee-25f1-4f73-8fb3-bf3fa04d7078 + version: -1 + name: 'Verify output contains: "Hello World"' + scriptName: VerifyHumanReadableContains + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "7" + scriptarguments: + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Hello World + separatecontext: false + view: |- + { + "position": { + "x": 480, + "y": 690 + } + } + note: false + timertriggers: [] + "36": + id: "36" + taskid: 795072c5-5b27-4d2a-83af-640e4d8147d0 + type: regular + task: + id: 795072c5-5b27-4d2a-83af-640e4d8147d0 + version: -1 + name: 'Verify output contains: "Lovely Day"' + scriptName: VerifyHumanReadableContains + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "18" + scriptarguments: + extend-context: {} + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Lovely Day + separatecontext: false + view: |- + { + "position": { + "x": 480, + "y": 1040 + } + } + note: false + timertriggers: [] + "37": + id: "37" + taskid: 78c0f142-5311-4600-8654-6232f4cfb25b + type: regular + task: + id: 78c0f142-5311-4600-8654-6232f4cfb25b + version: -1 + name: 'Verify output contains: "Hello World"' + scriptName: VerifyHumanReadableContains + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "25" + scriptarguments: + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Hello World + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 1535 + } + } + note: false + timertriggers: [] + "40": + id: "40" + taskid: d2ddf2d4-a568-45cb-84ce-0a6939e57ba3 + type: regular + task: + id: d2ddf2d4-a568-45cb-84ce-0a6939e57ba3 + version: -1 + name: 'Verify output contains: "Goodbye Please"' + scriptName: VerifyHumanReadableContains + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "30" + scriptarguments: + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Goodbye Please + separatecontext: false + view: |- + { + "position": { + "x": 480, + "y": 2410 + } + } + note: false + timertriggers: [] + "41": + id: "41" + taskid: 891181f3-e216-4d70-87b9-c1f3815ab22b + type: regular + task: + id: 891181f3-e216-4d70-87b9-c1f3815ab22b + version: -1 + name: 'Verify output contains: "Lovely Day"' + scriptName: VerifyHumanReadableContains + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "26" + scriptarguments: + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Lovely Day + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 1885 + } + } + note: false + timertriggers: [] + "42": + id: "42" + taskid: 574fd6e5-c63a-4e80-8ee0-86699dd4ac28 + type: regular + task: + id: 574fd6e5-c63a-4e80-8ee0-86699dd4ac28 + version: -1 + name: 'Verify output contains: "Goodbye Please"' + scriptName: VerifyHumanReadableContains + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "31" + scriptarguments: + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Goodbye Please + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 2760 + } + } + note: false + timertriggers: [] + "43": + id: "43" + taskid: 33bea087-a196-476e-84c5-34eca5a30aa8 + type: regular + task: + id: 33bea087-a196-476e-84c5-34eca5a30aa8 + version: -1 + name: 'Verify output contains: "Goodbye Please"' + scriptName: VerifyHumanReadableContains + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "33" + scriptarguments: + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: Goodbye Please + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 3110 + } + } + note: false + timertriggers: [] + "44": + id: "44" + taskid: 1881c0da-1a5e-478b-8420-4a259c3457eb + type: regular + task: + id: 1881c0da-1a5e-478b-8420-4a259c3457eb + version: -1 + name: 'Verify output contains: "test_file.txt"' + scriptName: VerifyHumanReadableContains + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "52" + scriptarguments: + humanReadableEntryId: + simple: ${lastCompletedTaskEntries.[0]} + substring: + simple: test_file.txt + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 3810 + } + } + note: false + timertriggers: [] + "45": + id: "45" + taskid: a40ccffd-feda-4d50-80ed-462e9e3844fc + type: regular + task: + id: a40ccffd-feda-4d50-80ed-462e9e3844fc + version: -1 + name: 'servicenow-upload-file test_file.txt to Ticket #0' + script: ServiceNow|||servicenow-upload-file + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "44" + scriptarguments: + file_id: + simple: ${File.EntryID} + file_name: {} + id: + simple: ${Ticket.[0].ID} + ticket_type: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 3635 + } + } + note: false + timertriggers: [] + "46": + id: "46" + taskid: 18460c88-cefb-42c5-829e-b7ae3b067072 + type: regular + task: + id: 18460c88-cefb-42c5-829e-b7ae3b067072 + version: -1 + name: VerifyContextFields - File Uploaded to War Room + scriptName: VerifyContextFields + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "45" + scriptarguments: + field1: + simple: File.Name + field2: {} + field3: {} + field4: {} + field5: {} + value1: + simple: test_file.txt + value2: {} + value3: {} + value4: {} + value5: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 3460 + } + } + note: false + timertriggers: [] + "48": + id: "48" + taskid: 9cf0ecb5-4ade-4c9f-8d22-d38c8c421808 + type: regular + task: + id: 9cf0ecb5-4ade-4c9f-8d22-d38c8c421808 + version: -1 + name: Get group by name + description: Return information about specific servicenow group + script: ServiceNow|||servicenow-query-groups + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "49" + scriptarguments: + group_id: {} + group_name: + simple: test1 + limit: {} + query: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 4510 + } + } + note: false + timertriggers: [] + "49": + id: "49" + taskid: f74af86d-da75-4050-87fa-f8891ded74e0 + type: regular + task: + id: f74af86d-da75-4050-87fa-f8891ded74e0 + version: -1 + name: VerifyContext + description: |- + Verifies path in context: + - Verifies path existence + - If matching object is an array: verify fields exists in each of the objects in the array + - If matching object is not an array: verify fields exists in matching object + - if 'expectedValue' is given: ensure that the given value is equal to the context path + scriptName: VerifyContext + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "53" + scriptarguments: + expectedValue: {} + fields: {} + path: + simple: ServiceNow.Group + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 4685 + } + } + note: false + timertriggers: [] + "50": + id: "50" + taskid: 8a079b91-fa9d-42f6-8450-c1de37d2fb07 + type: regular + task: + id: 8a079b91-fa9d-42f6-8450-c1de37d2fb07 + version: -1 + name: 'Get Ticket #0 with attachments' + description: Retrieve ticket information by specific ticket ID + script: ServiceNow|||servicenow-get-ticket + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "51" + scriptarguments: + get_attachments: + simple: "true" + id: + simple: ${Ticket.[0].ID} + number: {} + ticket_type: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 4160 + } + } + note: false + timertriggers: [] + "51": + id: "51" + taskid: 7a195718-e882-4017-8e01-3cfcb9f07276 + type: regular + task: + id: 7a195718-e882-4017-8e01-3cfcb9f07276 + version: -1 + name: Verify File + description: |- + Verifies path in context: + - Verifies path existence + - If matching object is an array: verify fields exists in each of the objects in the array + - If matching object is not an array: verify fields exists in matching object + - if 'expectedValue' is given: ensure that the given value is equal to the context path + scriptName: VerifyContext + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "48" + scriptarguments: + expectedValue: {} + fields: {} + path: + simple: File + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 4335 + } + } + note: false + timertriggers: [] + "52": + id: "52" + taskid: 825f7a30-af72-423b-89ff-fc1956af5045 + type: regular + task: + id: 825f7a30-af72-423b-89ff-fc1956af5045 + version: -1 + name: Delete File Context + description: Delete field from context + scriptName: DeleteContext + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "50" + scriptarguments: + all: {} + index: {} + key: + simple: File + keysToKeep: {} + subplaybook: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 3985 + } + } + note: false + timertriggers: [] + "53": + id: "53" + taskid: 4ffca856-7a55-4295-825e-36555db10917 + type: regular + task: + id: 4ffca856-7a55-4295-825e-36555db10917 + version: -1 + name: Query table + description: Query a specified table in ServiceNow + script: ServiceNow|||servicenow-query-table + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "54" + scriptarguments: + fields: + simple: sys_id,state,active + limit: {} + query: + simple: sys_idin${Ticket.ID} + table_name: + simple: incident + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 4860 + } + } + note: false + timertriggers: [] + "54": + id: "54" + taskid: 7ab6f886-dad3-4776-8972-53aacec27312 + type: regular + task: + id: 7ab6f886-dad3-4776-8972-53aacec27312 + version: -1 + name: VerifyContext + description: |- + Verifies path in context: + - Verifies path existence + - If matching object is an array: verify fields exists in each of the objects in the array + - If matching object is not an array: verify fields exists in matching object + - if 'expectedValue' is given: ensure that the given value is equal to the context path + scriptName: VerifyContext + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "55" + scriptarguments: + expectedValue: {} + fields: + simple: ID,state,active + path: + simple: ServiceNow.Record + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 5035 + } + } + note: false + timertriggers: [] + "55": + id: "55" + taskid: 7e21859c-9b5f-49ec-8e5d-9925de902eb4 + type: regular + task: + id: 7e21859c-9b5f-49ec-8e5d-9925de902eb4 + version: -1 + name: Delete the tickets + description: Delete a ticket from ServiceNow + script: ServiceNow|||servicenow-delete-ticket + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "69" + scriptarguments: + id: + simple: ${Ticket.ID} + ticket_type: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 5210 + } + } + note: false + timertriggers: [] + "56": + id: "56" + taskid: e5ce9d86-fd9e-42cf-8358-f6f45db45353 + type: regular + task: + id: e5ce9d86-fd9e-42cf-8358-f6f45db45353 + version: -1 + name: Create incident as record + description: Retrieve record information by specific record ID + script: ServiceNow|||servicenow-create-record + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "57" + scriptarguments: + custom_fields: {} + fields: + simple: short_description=Summary;assigned_to=Lucy Liu + table_name: + simple: incident + template: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 5560 + } + } + note: false + timertriggers: [] + "57": + id: "57" + taskid: 36c97825-5b42-4c94-8917-3e2043da6550 + type: regular + task: + id: 36c97825-5b42-4c94-8917-3e2043da6550 + version: -1 + name: Get the record + description: Retrieve record information by specific record ID + script: ServiceNow|||servicenow-get-record + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "58" + scriptarguments: + fields: + simple: short_description,assigned_to + id: + simple: ${ServiceNow.Record.ID} + table_name: + simple: incident + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 5735 + } + } + note: false + timertriggers: [] + "58": + id: "58" + taskid: 1f7f419f-c227-4b55-8584-e2905f78c05d + type: regular + task: + id: 1f7f419f-c227-4b55-8584-e2905f78c05d + version: -1 + name: VerifyContextFields + scriptName: VerifyContextFields + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "59" + scriptarguments: + field1: + simple: ServiceNow.Record.assigned_to + field2: + simple: ServiceNow.Record.short_description + field3: {} + field4: {} + field5: {} + value1: + simple: Lucy Liu + value2: + simple: Summary + value3: {} + value4: {} + value5: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 5910 + } + } + note: false + timertriggers: [] + "59": + id: "59" + taskid: ffcd8db4-5d7c-4288-8773-a0567edcc1d6 + type: regular + task: + id: ffcd8db4-5d7c-4288-8773-a0567edcc1d6 + version: -1 + name: Update record + description: Update specific ticket + script: ServiceNow|||servicenow-update-record + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "60" + scriptarguments: + custom_fields: {} + fields: + simple: assigned_to=John Smith;short_description=New + id: + simple: ${ServiceNow.Record.ID} + table_name: + simple: incident + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 6085 + } + } + note: false + timertriggers: [] + "60": + id: "60" + taskid: 5c442ca7-48f4-4419-8347-1a40d711d8f0 + type: regular + task: + id: 5c442ca7-48f4-4419-8347-1a40d711d8f0 + version: -1 + name: Get the record + description: Retrieve record information by specific record ID + script: ServiceNow|||servicenow-get-record + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "61" + scriptarguments: + fields: + simple: short_description,assigned_to + id: + simple: ${ServiceNow.Record.ID} + table_name: + simple: incident + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 6260 + } + } + note: false + timertriggers: [] + "61": + id: "61" + taskid: 57d1a8c4-1209-4150-856d-28fc3bed40f0 + type: regular + task: + id: 57d1a8c4-1209-4150-856d-28fc3bed40f0 + version: -1 + name: VerifyContextFields + scriptName: VerifyContextFields + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "62" + scriptarguments: + field1: + simple: ServiceNow.Record.assigned_to + field2: + simple: ServiceNow.Record.short_description + field3: {} + field4: {} + field5: {} + value1: + simple: John Smith + value2: + simple: New + value3: {} + value4: {} + value5: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 6435 + } + } + note: false + timertriggers: [] + "62": + id: "62" + taskid: 11c33afc-e222-4021-8dc6-d379b063c2b1 + type: regular + task: + id: 11c33afc-e222-4021-8dc6-d379b063c2b1 + version: -1 + name: Delete the record + description: Delete a record in a specified ServiceNow table + script: ServiceNow|||servicenow-delete-record + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "63" + scriptarguments: + id: + simple: ${ServiceNow.Record.ID} + table_name: + simple: incident + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 6610 + } + } + note: false + timertriggers: [] + "63": + id: "63" + taskid: f9da4899-51aa-4849-89d8-98f6e6f5a5d0 + type: regular + task: + id: f9da4899-51aa-4849-89d8-98f6e6f5a5d0 + version: -1 + name: servicenow-list-table-fields + description: List API fields for a specified ServiceNow table + script: ServiceNow|||servicenow-list-table-fields + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "64" + scriptarguments: + table_name: + simple: incident + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 6785 + } + } + note: false + timertriggers: [] + "64": + id: "64" + taskid: 559e1a7c-a93a-48fb-86c9-8f3a7c9d514a + type: regular + task: + id: 559e1a7c-a93a-48fb-86c9-8f3a7c9d514a + version: -1 + name: servicenow-query-computers + description: Query the cmdb_ci_computer table in ServiceNow + script: ServiceNow|||servicenow-query-computers + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "65" + scriptarguments: + asset_tag: {} + computer_id: {} + computer_name: {} + limit: + simple: "1" + query: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 6960 + } + } + note: false + timertriggers: [] + "65": + id: "65" + taskid: 3080d4c2-8ee6-4558-8ffa-03fbe0877727 + type: regular + task: + id: 3080d4c2-8ee6-4558-8ffa-03fbe0877727 + version: -1 + name: servicenow-query-groups + description: Query the sys_user_group table in ServiceNow + script: ServiceNow|||servicenow-query-groups + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "68" + scriptarguments: + group_id: {} + group_name: {} + limit: + simple: "1" + query: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 7135 + } + } + note: false + timertriggers: [] + "67": + id: "67" + taskid: b5fb1279-37be-432c-8b23-46e33a2507c8 + type: regular + task: + id: b5fb1279-37be-432c-8b23-46e33a2507c8 + version: -1 + name: VerifyContext + description: |- + Verifies path in context: + - Verifies path existence + - If matching object is an array: verify fields exists in each of the objects in the array + - If matching object is not an array: verify fields exists in matching object + - if 'expectedValue' is given: ensure that the given value is equal to the context path + scriptName: VerifyContext + type: regular + iscommand: false + brand: "" + scriptarguments: + expectedValue: {} + fields: + simple: Field,Computer,Group,User + path: + simple: ServiceNow + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 7485 + } + } + note: false + timertriggers: [] + "68": + id: "68" + taskid: d0effd4f-0e10-47b0-864f-612362afae46 + type: regular + task: + id: d0effd4f-0e10-47b0-864f-612362afae46 + version: -1 + name: servicenow-query-users + description: Query the sys_user table in ServiceNow + script: ServiceNow|||servicenow-query-users + type: regular + iscommand: true + brand: ServiceNow + nexttasks: + '#none#': + - "67" + scriptarguments: + limit: + simple: "1" + offset: {} + query: {} + user_id: {} + user_name: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 7310 + } + } + note: false + timertriggers: [] + "69": + id: "69" + taskid: df264a9e-7742-4efe-81f1-8284de528f74 + type: regular + task: + id: df264a9e-7742-4efe-81f1-8284de528f74 + version: -1 + name: DeleteContext + description: Delete field from context + scriptName: DeleteContext + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "56" + scriptarguments: + all: + simple: "yes" + index: {} + key: {} + keysToKeep: {} + subplaybook: {} + separatecontext: false + view: |- + { + "position": { + "x": 265, + "y": 5385 + } + } + note: false + timertriggers: [] +view: |- + { + "linkLabelsPosition": {}, + "paper": { + "dimensions": { + "height": 7530, + "width": 810, + "x": 50, + "y": 50 + } + } + } +inputs: [] +outputs: [] From 99286a9f294a2da2a6b444c40d43e82dc50eca6d Mon Sep 17 00:00:00 2001 From: Lior Blob Date: Tue, 27 Nov 2018 14:06:15 +0200 Subject: [PATCH 08/11] deprecate, rn --- Integrations/integration-ServiceNow.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Integrations/integration-ServiceNow.yml b/Integrations/integration-ServiceNow.yml index 421ec7171c6d..d0800a611f29 100644 --- a/Integrations/integration-ServiceNow.yml +++ b/Integrations/integration-ServiceNow.yml @@ -2986,6 +2986,7 @@ script: type: string description: Deprecated. Please use servicenow-upload-file instead. - name: servicenow-get-groups + deprecated: true arguments: - name: name required: true @@ -2999,6 +3000,7 @@ script: type: string description: Deprecated. Please use servicenow-query-groups instead. - name: servicenow-get-computer + deprecated: true arguments: - name: computerName required: true @@ -3297,4 +3299,5 @@ releaseNotes: "Added ability to retrieve records from any table generically in a servicenow-create -> servicenow-create-ticket, servicenow-create-record servicenow-update -> servicenow-update-ticket, servicenow-update-record, servicenow-query -> servicenow-query-tickets, servicenow-query-table + Added a command to view all API fields of a given table. Added commands to query computers, groups and users. Also output improvements." \ No newline at end of file From ea81b79d874007a2f0f31ef622a7211c4b69559b Mon Sep 17 00:00:00 2001 From: Lior Blob Date: Tue, 27 Nov 2018 15:44:02 +0200 Subject: [PATCH 09/11] more fixes --- Integrations/integration-ServiceNow.yml | 31 +++++++++++++++---------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/Integrations/integration-ServiceNow.yml b/Integrations/integration-ServiceNow.yml index d0800a611f29..5546e8779ab9 100644 --- a/Integrations/integration-ServiceNow.yml +++ b/Integrations/integration-ServiceNow.yml @@ -534,13 +534,17 @@ script: res = update(ticket_type, ticket_id ,fields, custom_fields, template) hr = get_ticket_human_readable(res['result'], ticket_type) + context = get_ticket_context(res['result'], ticket_type) entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('ServiceNow ticket updated successfully\nTicket type: ' + ticket_type, hr, removeNull=True) + 'HumanReadable': tableToMarkdown('ServiceNow ticket updated successfully\nTicket type: ' + ticket_type, hr, removeNull=True), + 'EntryContext': { + 'ServiceNow.Ticket(val.ID===obj.ID)': context + } } return entry @@ -569,7 +573,7 @@ script: 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('ServiceNow updated record', mapped_record, removeNull=True), + 'HumanReadable': tableToMarkdown('ServiceNow record updated successfully', mapped_record, removeNull=True), 'EntryContext': { 'ServiceNow.Record(val.ID===obj.ID)': createContext(mapped_record) } @@ -641,7 +645,7 @@ script: 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('ServiceNow created record', mapped_record, removeNull=True), + 'HumanReadable': tableToMarkdown('ServiceNow record created successfully', mapped_record, removeNull=True), 'EntryContext': { 'ServiceNow.Record(val.ID===obj.ID)': createContext(mapped_record) } @@ -710,7 +714,7 @@ script: 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('Link added to ServiceNow ticket', hr, removeNull=True) + 'HumanReadable': tableToMarkdown('Link successfully added to ServiceNow ticket', hr, removeNull=True) } return entry @@ -737,7 +741,7 @@ script: 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('Comment added to ServiceNow ticket', hr, removeNull=True) + 'HumanReadable': tableToMarkdown('Comment successfully added to ServiceNow ticket', hr, removeNull=True) } return entry @@ -872,8 +876,9 @@ script: 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], - 'HumanReadable': tableToMarkdown('File Uploaded', hr), + 'HumanReadable': tableToMarkdown('File uploaded successfully', hr), 'EntryContext': { + 'ServiceNow.Ticket(val.ID===obj.ID)' : context, 'Ticket(val.ID===obj.ID)' : context } } @@ -979,7 +984,7 @@ script: 'DisplayName': '{} - {}'.format(computer['asset_tag'], computer['name']), 'SupportGroup': computer['support_group'], 'OperatingSystem': computer['os'], - 'Company': computer['company'], + 'Company': computer.get('company', {}).get('value') if isinstance(computer['company'], dict) else computer['company'], 'AssignedTo': computer.get('assigned_to', {}).get('value') if isinstance(computer['assigned_to'], dict) else computer['assigned_to'], 'State': COMPUTER_STATUS.get(computer['install_status'], computer['install_status']), 'Cost': '{} {}'.format(computer['cost'], computer['cost_cc']), @@ -2944,13 +2949,13 @@ script: description: Filename of uploaded file to override the existing file name in entry outputs: - - contextPath: Ticket.File.Filename + - contextPath: ServiceNow.Ticket.File.Filename description: Name of the file type: string - - contextPath: Ticket.File.Link + - contextPath: ServiceNow.Ticket.File.Link description: Download link for the file type: string - - contextPath: Ticket.File.SystemID + - contextPath: ServiceNow.Ticket.File.SystemID description: System ID of the file type: string description: Upload a file to a specific ticket @@ -3293,11 +3298,13 @@ script: description: Query the sys_user table in ServiceNow isfetch: true runonce: false -releaseNotes: "Added ability to retrieve records from any table generically in addition to tickets. +releaseNotes: "Added ability to get,update,create and delete records from any table generically in addition to tickets. the commands are separated in the following way: servicenow-get -> servicenow-get-ticket, servicenow-get-record servicenow-create -> servicenow-create-ticket, servicenow-create-record servicenow-update -> servicenow-update-ticket, servicenow-update-record, servicenow-query -> servicenow-query-tickets, servicenow-query-table Added a command to view all API fields of a given table. - Added commands to query computers, groups and users. Also output improvements." \ No newline at end of file + Added commands to query computers, groups and users. Also output improvements." +tests: + - 'Forgive me for my sins but I did not create any test' \ No newline at end of file From 59f87d53294939ab2804e44e1914cf03ac25a197 Mon Sep 17 00:00:00 2001 From: Lior Blob Date: Tue, 27 Nov 2018 15:46:05 +0200 Subject: [PATCH 10/11] skip integration --- Tests/conf.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/conf.json b/Tests/conf.json index 5604067ae09c..42ccf19548c0 100644 --- a/Tests/conf.json +++ b/Tests/conf.json @@ -694,6 +694,7 @@ "LogRhythm": "DevOps investigation", "Phish.AI": "Need to check the reason for skipping", "Dell Secureworks": "Instance locally installed on @liorblob PC", - "Service Manager": "Expired license" + "Service Manager": "Expired license", + "ServiceNow": "Instance goes to hibernate every few hours" } } From a9286fa5bb2d2b3725cf44981cd7903c72ecc45d Mon Sep 17 00:00:00 2001 From: Lior Blob Date: Tue, 27 Nov 2018 15:57:20 +0200 Subject: [PATCH 11/11] oops --- Tests/conf.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Tests/conf.json b/Tests/conf.json index 6ae40b7842d6..5ffec76727d7 100644 --- a/Tests/conf.json +++ b/Tests/conf.json @@ -758,7 +758,6 @@ "entity_enrichment_generic_test": "Need to check the reason for skipping", "search_endpoints_by_hash_-_generic_-_test": "Need to check the reason for skipping", "ArcSight Logger test": "Possibly outdated API calls", - "Anomali_ThreatStream_Test": "Need to check the reason for skipping", "Centreon-Test-Playbook": "Need to check the reason for skipping", "TruSTAR Test": "Need to check the reason for skipping", "AlphaSOC-Wisdom-Test": "Need to check the reason for skipping", @@ -771,9 +770,6 @@ "skipped_integrations": { "Anomali ThreatStream": "Instance is down", "McAfee Advanced Threat Defense": "No Credentials", - "Symantec Endpoint Protection": "DevOps investigation", - "RedLock": "Don't have alerts", - "Symantec Messaging Gateway": "DevOps investigation", "Cylance Protect": "Under development", "VMware": "DevOps investigation", "Farsight DNSDB": "No instance",