Skip to content

Commit 19d30bb

Browse files
authored
Merge pull request #129 from Snow-Shell/advanced-filtering
Advanced filtering and sorting
2 parents 49f465c + acc0a5f commit 19d30bb

13 files changed

+669
-73
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
## v2.2
2+
- Add advanced filtering and sorting. Initially implemented with `New-ServiceNowQuery` and `Get-ServiceNowRecord`. Filter with many different comparison operators as well as 'and', 'or', and 'group'ing. Sort ascending or descending against multiple fields. Comparison operators are the same as PowerShell for ease of use. Please use the GitHub Discussions section to provide feedback, thoughts, etc.
3+
- Add `Get-ServiceNowRecord`. This function implements the new advanced filtering and sorting. As long as you know your table name, this can replace all other Get functions.
4+
- Enumerate implemented tables and advanced filtering operators in a json config to easily manage going forward; make available via script scoped variables.
5+
Be able to reference types from this config per table, removing the need to have separate Get functions for every table.
6+
- Add type for catalog task
7+
- Fix error when getting an empty result from the api and performing a type lookup
8+
- Rename `RequestItem` to `RequestedItem` which is the actual name. Function aliases created.
9+
110
## v2.1
211
- Add proxy support to `New-ServiceNowSession`, [#97](https://github.com/Snow-Shell/servicenow-powershell/issues/97).
312

Readme.md

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Building on the great work the community has done thus far, a lot of new updates
1818

1919
## Requirements
2020

21-
Requires PowerShell 3.0 or above as this is when `Invoke-RestMethod` was introduced.
21+
Requires PowerShell 5.1 or above.
2222

2323
Requires authorization in your ServiceNow tenant. Due to the custom nature of ServiceNow your organization may have REST access restricted. The following are some tips to ask for if you're having to go to your admin for access:
2424

@@ -32,25 +32,52 @@ The ServiceNow module should be installed from the PowerShell Gallery with `inst
3232

3333
### Creating a new session
3434

35+
Creating a new session will create a script scoped variable `$ServiceNowSession` which will be used by default in other functions.
36+
37+
Basic authentication with just a credential...
3538
```PowerShell
36-
New-ServiceNowSession -url InstanceName.service-now.com -Credentials (Get-Credential)
39+
$params @{
40+
Url = 'instance.service-now.com'
41+
Credential = $userCred
42+
}
43+
New-ServiceNowSession @params
3744
```
3845

39-
This example is using basic authentication, but OAuth is available as well; see the built-in help for `New-ServiceNowSession`. All examples below assume a new session has already been created.
46+
Oauth authentication with user credential as well as application/client credential. The application/client credential can be found in the System OAuth->Application Registry section of ServiceNow.
47+
```PowerShell
48+
$params @{
49+
Url = 'instance.service-now.com'
50+
Credential = $userCred
51+
ClientCredential = $clientCred
52+
}
53+
New-ServiceNowSession @params
54+
```
4055

41-
### Example - Retrieving an Incident Containing the Word 'PowerShell'
56+
All examples below assume a new session has already been created.
57+
58+
### Getting incidents opened in the last 30 days
59+
```PowerShell
60+
$filter = @('opened_at', '-ge', 'javascript:gs.daysAgoEnd(30)')
61+
Get-ServiceNowRecord -Table incident -Filter $filter
62+
```
63+
64+
### Retrieving an Incident Containing the Word 'PowerShell'
4265

4366
```PowerShell
4467
Get-ServiceNowIncident -MatchContains @{short_description='PowerShell'}
4568
```
69+
or new with v2.2
70+
```PowerShell
71+
Get-ServiceNowRecord -Table incident -Filter @('short_description','-eq','PowerShell')
72+
```
4673

47-
### Example - Update a Ticket
74+
### Update a Ticket
4875

4976
```PowerShell
5077
Get-ServiceNowIncident -Limit 1 -MatchContains @{short_description='PowerShell'} | Update-ServiceNowIncident -Values @{comments='Updated via PowerShell'}
5178
```
5279

53-
### Example - Creating a Incident with custom table entries
80+
### Creating an Incident with custom table entries
5481

5582
```PowerShell
5683
$IncidentParams = @{Caller = "UserName"

ServiceNow/Private/Invoke-ServiceNowRestMethod.ps1

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function Invoke-ServiceNowRestMethod {
2323
# Name of the table we're querying (e.g. incidents)
2424
[parameter(Mandatory, ParameterSetName = 'Table')]
2525
[ValidateNotNullOrEmpty()]
26+
[Alias('sys_class_name')]
2627
[string] $Table,
2728

2829
[parameter(ParameterSetName = 'Table')]
@@ -39,6 +40,13 @@ function Invoke-ServiceNowRestMethod {
3940
[parameter()]
4041
[hashtable] $Values,
4142

43+
[parameter()]
44+
[System.Collections.ArrayList] $Filter,
45+
46+
[parameter()]
47+
[ValidateNotNullOrEmpty()]
48+
[System.Collections.ArrayList] $Sort = @('opened_at', 'desc'),
49+
4250
# sysparm_query param in the format of a ServiceNow encoded query string (see http://wiki.servicenow.com/index.php?title=Encoded_Query_Strings)
4351
[Parameter()]
4452
[string] $Query,
@@ -82,7 +90,15 @@ function Invoke-ServiceNowRestMethod {
8290
$params.ContentType = 'application/json'
8391

8492
if ( $Table ) {
85-
$params.Uri += "/table/$Table"
93+
# table can either be the actual table name or class name
94+
# look up the actual table name
95+
$tableName = $script:ServiceNowTable | Where-Object { $_.Name -eq $Table -or $_.ClassName -eq $Table } | Select-Object -ExpandProperty Name
96+
# if not in our lookup, just use the table name as provided
97+
if ( -not $tableName ) {
98+
$tableName = $Table
99+
}
100+
101+
$params.Uri += "/table/$tableName"
86102
if ( $SysId ) {
87103
$params.Uri += "/$SysId"
88104
}
@@ -135,6 +151,8 @@ function Invoke-ServiceNowRestMethod {
135151
# Populate the query
136152
if ($Query) {
137153
$Body.sysparm_query = $Query
154+
} else {
155+
$body.sysparm_query = (New-ServiceNowQuery -Filter $Filter -Sort $Sort)
138156
}
139157

140158
if ($Properties) {
@@ -159,7 +177,7 @@ function Invoke-ServiceNowRestMethod {
159177

160178
switch ($Method) {
161179
'Get' {
162-
if ( $response.result ) {
180+
if ( $response.PSobject.Properties.Name -contains "result" ) {
163181

164182
$result = $response | Select-Object -ExpandProperty result
165183
$ConvertToDateField = @('closed_at', 'expected_start', 'follow_up', 'opened_at', 'sys_created_on', 'sys_updated_on', 'work_end', 'work_start')
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<#
2+
.SYNOPSIS
3+
Retrieves records for the specified table
4+
5+
.DESCRIPTION
6+
Retrieve records from any table with the option to filter, sort, and choose fields.
7+
Given you know the table name, you shouldn't need any other 'Get-' function.
8+
9+
.PARAMETER Table
10+
Name of the table to be queried, by either table name or class name
11+
12+
.PARAMETER Properties
13+
Limit the fields returned to this list
14+
15+
.PARAMETER Filter
16+
Array or multidimensional array of fields and values to filter on.
17+
Each array should be of the format @(field, comparison operator, value) separated by a join, either 'and', 'or', or 'group'.
18+
For a complete list of comparison operators, see $script:ServiceNowOperator and use Name in your filter.
19+
See the examples.
20+
Also, see https://docs.servicenow.com/bundle/quebec-platform-user-interface/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html
21+
for how to represent date values with javascript.
22+
23+
.PARAMETER Sort
24+
Array or multidimensional array of fields to sort on.
25+
Each array should be of the format @(field, asc/desc).
26+
27+
.PARAMETER DisplayValues
28+
Option to display values for reference fields.
29+
'false' will only retrieve the reference
30+
'true' will only retrieve the underlying value
31+
'all' will retrieve both. This is helpful when trying to translate values for a query.
32+
33+
.PARAMETER Connection
34+
Azure Automation Connection object containing username, password, and URL for the ServiceNow instance
35+
36+
.PARAMETER ServiceNowSession
37+
ServiceNow session created by New-ServiceNowSession. Will default to script-level variable $ServiceNowSession.
38+
39+
.EXAMPLE
40+
Get-ServiceNowRecord -Table incident -Filter @('state', '-eq', '1'), 'or', @('short_description','-like', 'powershell')
41+
Get incident records where state equals New or short description contains the word powershell
42+
43+
.EXAMPLE
44+
$filter = @('state', '-eq', '1'),
45+
'and',
46+
@('short_description','-like', 'powershell'),
47+
'group',
48+
@('state', '-eq', '2')
49+
PS > Get-ServiceNowRecord -Table incident -Filter $filter
50+
Get incident records where state equals New and short description contains the word powershell or state equals In Progress.
51+
The first 2 filters are combined and then or'd against the last.
52+
53+
.EXAMPLE
54+
Get-ServiceNowRecord -Table incident -Filter @('state', '-eq', '1') -Sort @('opened_at', 'desc'), @('state')
55+
Get incident records where state equals New and first sort by the field opened_at descending and then sort by the field state ascending
56+
57+
.EXAMPLE
58+
Get-ServiceNowRecord -Table 'change request' -Filter @('opened_at', '-ge', 'javascript:gs.daysAgoEnd(30)')
59+
Get change requests opened in the last 30 days. Use class name as opposed to table name.
60+
61+
.INPUTS
62+
None
63+
64+
.OUTPUTS
65+
System.Management.Automation.PSCustomObject
66+
67+
.LINK
68+
https://docs.servicenow.com/bundle/quebec-platform-user-interface/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html
69+
#>
70+
function Get-ServiceNowRecord {
71+
72+
[OutputType([System.Management.Automation.PSCustomObject])]
73+
[CmdletBinding(DefaultParameterSetName = 'SessionFilter', SupportsPaging)]
74+
75+
Param (
76+
[parameter(Mandatory)]
77+
[ValidateNotNullOrEmpty()]
78+
[Alias('sys_class_name')]
79+
[string] $Table,
80+
81+
[Parameter()]
82+
[Alias('Fields')]
83+
[string[]] $Properties,
84+
85+
[parameter(ParameterSetName = 'AutomationFilter')]
86+
[parameter(ParameterSetName = 'SessionFilter')]
87+
[System.Collections.ArrayList] $Filter,
88+
89+
[parameter(ParameterSetName = 'AutomationFilter')]
90+
[parameter(ParameterSetName = 'SessionFilter')]
91+
[ValidateNotNullOrEmpty()]
92+
[System.Collections.ArrayList] $Sort,
93+
94+
[Parameter()]
95+
[ValidateSet('true', 'false', 'all')]
96+
[string] $DisplayValues = 'true',
97+
98+
[Parameter(Mandatory, ParameterSetName = 'AutomationQuery')]
99+
[parameter(Mandatory, ParameterSetName = 'AutomationFilter')]
100+
[ValidateNotNullOrEmpty()]
101+
[hashtable] $Connection,
102+
103+
[Parameter(ParameterSetName = 'SessionQuery')]
104+
[Parameter(ParameterSetName = 'SessionFilter')]
105+
[ValidateNotNullOrEmpty()]
106+
[hashtable] $ServiceNowSession = $script:ServiceNowSession
107+
)
108+
109+
$result = Invoke-ServiceNowRestMethod @PSBoundParameters
110+
111+
If ( $result -and -not $Properties) {
112+
$type = $script:ServiceNowTable | Where-Object {$_.Name -eq $Table -or $_.ClassName -eq $Table} | Select-Object -ExpandProperty Type
113+
if ($type) {
114+
$result | ForEach-Object { $_.PSObject.TypeNames.Insert(0, $type) }
115+
}
116+
}
117+
118+
$result
119+
}

ServiceNow/Public/Get-ServiceNowRequestItem.ps1 renamed to ServiceNow/Public/Get-ServiceNowRequestedItem.ps1

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
function Get-ServiceNowRequestItem {
1+
function Get-ServiceNowRequestedItem {
22
<#
33
.SYNOPSIS
4-
Query for Request Item (RITM) tickets.
4+
Query for Requested Item (RITM) tickets.
55
66
.DESCRIPTION
7-
Query for Request Item (RITM) tickets from the sc_req_item table.
7+
Query for Requested Item (RITM) tickets from the sc_req_item table.
88
99
.EXAMPLE
10-
Get-ServiceNowRequestItem -MatchExact @{number='RITM0000001'}
10+
Get-ServiceNowRequestedItem -MatchExact @{number='RITM0000001'}
1111
1212
Return the details for RITM0000001
1313

ServiceNow/Public/Get-ServiceNowTableEntry.ps1

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,52 +23,52 @@ function Get-ServiceNowTableEntry {
2323
param(
2424
# Table containing the entry we're deleting
2525
[parameter(Mandatory)]
26-
[string]$Table,
26+
[string] $Table,
2727

2828
# Machine name of the field to order by
2929
[parameter()]
30-
[string]$OrderBy = 'opened_at',
30+
[string] $OrderBy = 'opened_at',
3131

3232
# Direction of ordering (Desc/Asc)
3333
[parameter()]
3434
[ValidateSet('Desc', 'Asc')]
35-
[string]$OrderDirection = 'Desc',
35+
[string] $OrderDirection = 'Desc',
3636

3737
# Maximum number of records to return
3838
[parameter()]
39-
[int]$Limit,
39+
[int] $Limit,
4040

4141
# Fields to return
4242
[Parameter()]
4343
[Alias('Fields')]
44-
[string[]]$Properties,
44+
[string[]] $Properties,
4545

4646
# Hashtable containing machine field names and values returned must match exactly (will be combined with AND)
4747
[parameter()]
48-
[hashtable]$MatchExact = @{},
48+
[hashtable] $MatchExact = @{},
4949

5050
# Hashtable containing machine field names and values returned rows must contain (will be combined with AND)
5151
[parameter()]
52-
[hashtable]$MatchContains = @{},
52+
[hashtable] $MatchContains = @{},
5353

5454
# Whether or not to show human readable display values instead of machine values
5555
[parameter()]
5656
[ValidateSet('true', 'false', 'all')]
57-
[string]$DisplayValues = 'true',
57+
[string] $DisplayValues = 'true',
5858

5959
[Parameter(ParameterSetName = 'SpecifyConnectionFields', Mandatory)]
6060
[ValidateNotNullOrEmpty()]
6161
[Alias('ServiceNowCredential')]
62-
[PSCredential]$Credential,
62+
[PSCredential] $Credential,
6363

6464
[Parameter(ParameterSetName = 'SpecifyConnectionFields', Mandatory)]
6565
[ValidateScript( { $_ | Test-ServiceNowURL })]
6666
[Alias('Url')]
67-
[string]$ServiceNowURL,
67+
[string] $ServiceNowURL,
6868

6969
[Parameter(ParameterSetName = 'UseConnectionObject', Mandatory)]
7070
[ValidateNotNullOrEmpty()]
71-
[hashtable]$Connection,
71+
[hashtable] $Connection,
7272

7373
[Parameter(ParameterSetName = 'Session')]
7474
[ValidateNotNullOrEmpty()]

0 commit comments

Comments
 (0)