lex-service_now 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/CODEOWNERS +7 -0
- data/.github/dependabot.yml +18 -0
- data/.github/workflows/ci.yml +34 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +56 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +13 -0
- data/LICENSE +21 -0
- data/Rakefile +8 -0
- data/lex-service_now.gemspec +35 -0
- data/lib/legion/extensions/service_now/account/runners/account.rb +47 -0
- data/lib/legion/extensions/service_now/change/runners/change.rb +115 -0
- data/lib/legion/extensions/service_now/client.rb +36 -0
- data/lib/legion/extensions/service_now/cmdb/instance/runners/instance.rb +67 -0
- data/lib/legion/extensions/service_now/cmdb/meta/runners/meta.rb +30 -0
- data/lib/legion/extensions/service_now/helpers/client.rb +45 -0
- data/lib/legion/extensions/service_now/knowledge/runners/knowledge.rb +51 -0
- data/lib/legion/extensions/service_now/service_catalog/runners/service_catalog.rb +83 -0
- data/lib/legion/extensions/service_now/skills/change_request.rb +59 -0
- data/lib/legion/extensions/service_now/skills/cmdb_query.rb +51 -0
- data/lib/legion/extensions/service_now/skills/incident.rb +59 -0
- data/lib/legion/extensions/service_now/skills/knowledge.rb +51 -0
- data/lib/legion/extensions/service_now/skills/service_catalog.rb +60 -0
- data/lib/legion/extensions/service_now/version.rb +9 -0
- data/lib/legion/extensions/service_now.rb +29 -0
- metadata +183 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9850b5ae25b0769c3e26b07fac9cbfe6fb6913e718e0eaba29c2a62e42bdc155
|
|
4
|
+
data.tar.gz: 72b828b004be9fe534c51a677c011a6cbe8d6470f91e7b25912d42b1ee1eb956
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6d728a8a52d1178d62eaa70b3d1c90b41a22fff61e9a8dc6916369eeb508e523f16b09c7e12262c8d6dec3c13ad62892ccfbffe6a8dc33ceca56379b26cff5fd
|
|
7
|
+
data.tar.gz: ae69b9ad088f9f5ed8fb5420cb6dea76eb6c083152af6800af83d74a34c8f32f1d093da9a3ede7b8b6f95ece399f65f8a3c32d3c0e2e380afbf12c59acc37f1b
|
data/.github/CODEOWNERS
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: bundler
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
day: monday
|
|
8
|
+
open-pull-requests-limit: 5
|
|
9
|
+
labels:
|
|
10
|
+
- "type:dependencies"
|
|
11
|
+
- package-ecosystem: github-actions
|
|
12
|
+
directory: /
|
|
13
|
+
schedule:
|
|
14
|
+
interval: weekly
|
|
15
|
+
day: monday
|
|
16
|
+
open-pull-requests-limit: 5
|
|
17
|
+
labels:
|
|
18
|
+
- "type:dependencies"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
schedule:
|
|
7
|
+
- cron: '0 9 * * 1'
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
ci:
|
|
11
|
+
uses: LegionIO/.github/.github/workflows/ci.yml@main
|
|
12
|
+
|
|
13
|
+
lint:
|
|
14
|
+
uses: LegionIO/.github/.github/workflows/lint-patterns.yml@main
|
|
15
|
+
|
|
16
|
+
security:
|
|
17
|
+
uses: LegionIO/.github/.github/workflows/security-scan.yml@main
|
|
18
|
+
|
|
19
|
+
version-changelog:
|
|
20
|
+
uses: LegionIO/.github/.github/workflows/version-changelog.yml@main
|
|
21
|
+
|
|
22
|
+
dependency-review:
|
|
23
|
+
uses: LegionIO/.github/.github/workflows/dependency-review.yml@main
|
|
24
|
+
|
|
25
|
+
stale:
|
|
26
|
+
if: github.event_name == 'schedule'
|
|
27
|
+
uses: LegionIO/.github/.github/workflows/stale.yml@main
|
|
28
|
+
|
|
29
|
+
release:
|
|
30
|
+
needs: [ci, lint]
|
|
31
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
32
|
+
uses: LegionIO/.github/.github/workflows/release.yml@main
|
|
33
|
+
secrets:
|
|
34
|
+
rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.4
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
|
|
6
|
+
Layout/LineLength:
|
|
7
|
+
Max: 160
|
|
8
|
+
|
|
9
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
|
10
|
+
EnforcedStyle: space
|
|
11
|
+
|
|
12
|
+
Layout/HashAlignment:
|
|
13
|
+
EnforcedHashRocketStyle: table
|
|
14
|
+
EnforcedColonStyle: table
|
|
15
|
+
|
|
16
|
+
Metrics/MethodLength:
|
|
17
|
+
Max: 50
|
|
18
|
+
|
|
19
|
+
Metrics/ClassLength:
|
|
20
|
+
Max: 1500
|
|
21
|
+
|
|
22
|
+
Metrics/ModuleLength:
|
|
23
|
+
Max: 1500
|
|
24
|
+
|
|
25
|
+
Metrics/BlockLength:
|
|
26
|
+
Max: 40
|
|
27
|
+
Exclude:
|
|
28
|
+
- 'spec/**/*'
|
|
29
|
+
|
|
30
|
+
Metrics/ParameterLists:
|
|
31
|
+
Max: 8
|
|
32
|
+
|
|
33
|
+
Naming/MethodParameterName:
|
|
34
|
+
MinNameLength: 2
|
|
35
|
+
|
|
36
|
+
Metrics/AbcSize:
|
|
37
|
+
Max: 60
|
|
38
|
+
|
|
39
|
+
Metrics/CyclomaticComplexity:
|
|
40
|
+
Max: 15
|
|
41
|
+
|
|
42
|
+
Metrics/PerceivedComplexity:
|
|
43
|
+
Max: 17
|
|
44
|
+
|
|
45
|
+
Style/Documentation:
|
|
46
|
+
Enabled: false
|
|
47
|
+
|
|
48
|
+
Style/SymbolArray:
|
|
49
|
+
Enabled: true
|
|
50
|
+
|
|
51
|
+
Style/FrozenStringLiteralComment:
|
|
52
|
+
Enabled: true
|
|
53
|
+
EnforcedStyle: always
|
|
54
|
+
|
|
55
|
+
Naming/FileName:
|
|
56
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-04-22
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Initial release
|
|
7
|
+
- Change Management API (14 methods)
|
|
8
|
+
- CMDB Instance API (7 methods)
|
|
9
|
+
- CMDB Meta API (2 methods)
|
|
10
|
+
- Knowledge API (5 methods)
|
|
11
|
+
- Service Catalog API (11 methods)
|
|
12
|
+
- Account API (4 methods)
|
|
13
|
+
- LLM skill triggers for Incident, ChangeRequest, CmdbQuery, Knowledge, ServiceCatalog
|
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Esity
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/service_now/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-service_now'
|
|
7
|
+
spec.version = Legion::Extensions::ServiceNow::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX::ServiceNow'
|
|
12
|
+
spec.description = 'Connects Legion to ServiceNow via Change Management, CMDB, Knowledge, Service Catalog, and Account REST APIs'
|
|
13
|
+
spec.homepage = 'https://github.com/LegionIO/lex-service_now'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
spec.required_ruby_version = '>= 3.4'
|
|
16
|
+
|
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-service_now'
|
|
19
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-service_now/blob/main/CHANGELOG.md'
|
|
20
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
21
|
+
|
|
22
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
23
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
24
|
+
end
|
|
25
|
+
spec.require_paths = ['lib']
|
|
26
|
+
|
|
27
|
+
spec.add_dependency 'faraday', '>= 2.0'
|
|
28
|
+
spec.add_dependency 'legion-cache', '>= 1.3.11'
|
|
29
|
+
spec.add_dependency 'legion-crypt', '>= 1.4.9'
|
|
30
|
+
spec.add_dependency 'legion-data', '>= 1.4.17'
|
|
31
|
+
spec.add_dependency 'legion-json', '>= 1.2.1'
|
|
32
|
+
spec.add_dependency 'legion-logging', '>= 1.3.2'
|
|
33
|
+
spec.add_dependency 'legion-settings', '>= 1.3.14'
|
|
34
|
+
spec.add_dependency 'legion-transport', '>= 1.3.9'
|
|
35
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
module Account
|
|
7
|
+
module Runners
|
|
8
|
+
module Account
|
|
9
|
+
include Legion::Extensions::ServiceNow::Helpers::Client
|
|
10
|
+
|
|
11
|
+
def list_accounts(sysparm_limit: 100, sysparm_offset: 0, sysparm_query: nil, **)
|
|
12
|
+
params = { sysparm_limit: sysparm_limit, sysparm_offset: sysparm_offset }
|
|
13
|
+
params[:sysparm_query] = sysparm_query if sysparm_query
|
|
14
|
+
resp = connection(**).get('/api/now/account', params)
|
|
15
|
+
{ accounts: resp.body['result'] }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def get_account(sys_id:, **)
|
|
19
|
+
resp = connection(**).get("/api/now/account/#{sys_id}")
|
|
20
|
+
{ account: resp.body['result'] }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def create_account(name:, phone: nil, email: nil, **)
|
|
24
|
+
body = { name: name }
|
|
25
|
+
body[:phone] = phone if phone
|
|
26
|
+
body[:email] = email if email
|
|
27
|
+
resp = connection(**).post('/api/now/account', body)
|
|
28
|
+
{ account: resp.body['result'] }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def update_account(sys_id:, name: nil, phone: nil, email: nil, **)
|
|
32
|
+
body = {}
|
|
33
|
+
body[:name] = name if name
|
|
34
|
+
body[:phone] = phone if phone
|
|
35
|
+
body[:email] = email if email
|
|
36
|
+
resp = connection(**).patch("/api/now/account/#{sys_id}", body)
|
|
37
|
+
{ account: resp.body['result'] }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
41
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
module Change
|
|
7
|
+
module Runners
|
|
8
|
+
module Change
|
|
9
|
+
include Legion::Extensions::ServiceNow::Helpers::Client
|
|
10
|
+
|
|
11
|
+
def list_changes(sysparm_limit: 100, sysparm_offset: 0, sysparm_query: nil, **)
|
|
12
|
+
params = { sysparm_limit: sysparm_limit, sysparm_offset: sysparm_offset }
|
|
13
|
+
params[:sysparm_query] = sysparm_query if sysparm_query
|
|
14
|
+
resp = connection(**).get('/api/sn_chg_rest/change', params)
|
|
15
|
+
{ changes: resp.body['result'] }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create_normal(short_description:, description: nil, assignment_group: nil,
|
|
19
|
+
start_date: nil, end_date: nil, **)
|
|
20
|
+
body = { short_description: short_description }
|
|
21
|
+
body[:description] = description if description
|
|
22
|
+
body[:assignment_group] = assignment_group if assignment_group
|
|
23
|
+
body[:start_date] = start_date if start_date
|
|
24
|
+
body[:end_date] = end_date if end_date
|
|
25
|
+
resp = connection(**).post('/api/sn_chg_rest/change/normal', body)
|
|
26
|
+
{ change: resp.body['result'] }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create_emergency(short_description:, description: nil, assignment_group: nil, **)
|
|
30
|
+
body = { short_description: short_description }
|
|
31
|
+
body[:description] = description if description
|
|
32
|
+
body[:assignment_group] = assignment_group if assignment_group
|
|
33
|
+
resp = connection(**).post('/api/sn_chg_rest/change/emergency', body)
|
|
34
|
+
{ change: resp.body['result'] }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def create_standard(short_description:, description: nil, assignment_group: nil, **)
|
|
38
|
+
body = { short_description: short_description }
|
|
39
|
+
body[:description] = description if description
|
|
40
|
+
body[:assignment_group] = assignment_group if assignment_group
|
|
41
|
+
resp = connection(**).post('/api/sn_chg_rest/change/standard', body)
|
|
42
|
+
{ change: resp.body['result'] }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def get_change(id:, **)
|
|
46
|
+
resp = connection(**).get("/api/sn_chg_rest/change/#{id}")
|
|
47
|
+
{ change: resp.body['result'] }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def update_change(id:, short_description: nil, description: nil, state: nil,
|
|
51
|
+
assignment_group: nil, start_date: nil, end_date: nil, **)
|
|
52
|
+
body = {}
|
|
53
|
+
body[:short_description] = short_description if short_description
|
|
54
|
+
body[:description] = description if description
|
|
55
|
+
body[:state] = state if state
|
|
56
|
+
body[:assignment_group] = assignment_group if assignment_group
|
|
57
|
+
body[:start_date] = start_date if start_date
|
|
58
|
+
body[:end_date] = end_date if end_date
|
|
59
|
+
resp = connection(**).patch("/api/sn_chg_rest/change/#{id}", body)
|
|
60
|
+
{ change: resp.body['result'] }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def delete_change(id:, **)
|
|
64
|
+
resp = connection(**).delete("/api/sn_chg_rest/change/#{id}")
|
|
65
|
+
{ deleted: resp.status == 204, id: id }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def list_tasks(id:, **)
|
|
69
|
+
resp = connection(**).get("/api/sn_chg_rest/change/#{id}/task")
|
|
70
|
+
{ tasks: resp.body['result'] }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def create_task(id:, short_description:, **)
|
|
74
|
+
body = { short_description: short_description }
|
|
75
|
+
resp = connection(**).post("/api/sn_chg_rest/change/#{id}/task", body)
|
|
76
|
+
{ task: resp.body['result'] }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def update_task(id:, task_id:, short_description: nil, state: nil, assigned_to: nil, **)
|
|
80
|
+
body = {}
|
|
81
|
+
body[:short_description] = short_description if short_description
|
|
82
|
+
body[:state] = state if state
|
|
83
|
+
body[:assigned_to] = assigned_to if assigned_to
|
|
84
|
+
resp = connection(**).patch("/api/sn_chg_rest/change/#{id}/task/#{task_id}", body)
|
|
85
|
+
{ task: resp.body['result'] }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def delete_task(id:, task_id:, **)
|
|
89
|
+
resp = connection(**).delete("/api/sn_chg_rest/change/#{id}/task/#{task_id}")
|
|
90
|
+
{ deleted: resp.status == 204, task_id: task_id }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def get_conflicts(id:, **)
|
|
94
|
+
resp = connection(**).get("/api/sn_chg_rest/change/#{id}/conflict")
|
|
95
|
+
{ conflicts: resp.body['result'] }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def calculate_conflicts(id:, **)
|
|
99
|
+
resp = connection(**).post("/api/sn_chg_rest/change/#{id}/conflict", {})
|
|
100
|
+
{ conflicts: resp.body['result'] }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def get_approvals(id:, **)
|
|
104
|
+
resp = connection(**).get("/api/sn_chg_rest/change/#{id}/approvals")
|
|
105
|
+
{ approvals: resp.body['result'] }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
109
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
class Client
|
|
7
|
+
include Helpers::Client
|
|
8
|
+
include Change::Runners::Change
|
|
9
|
+
include Cmdb::Instance::Runners::Instance
|
|
10
|
+
include Cmdb::Meta::Runners::Meta
|
|
11
|
+
include Knowledge::Runners::Knowledge
|
|
12
|
+
include ServiceCatalog::Runners::ServiceCatalog
|
|
13
|
+
include Account::Runners::Account
|
|
14
|
+
|
|
15
|
+
attr_reader :opts
|
|
16
|
+
|
|
17
|
+
def initialize(url: nil, client_id: nil, client_secret: nil,
|
|
18
|
+
token: nil, username: nil, password: nil, **extra)
|
|
19
|
+
@opts = {
|
|
20
|
+
url: url,
|
|
21
|
+
client_id: client_id,
|
|
22
|
+
client_secret: client_secret,
|
|
23
|
+
token: token,
|
|
24
|
+
username: username,
|
|
25
|
+
password: password,
|
|
26
|
+
**extra
|
|
27
|
+
}.compact
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def connection(**override)
|
|
31
|
+
super(**@opts, **override)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
module Cmdb
|
|
7
|
+
module Instance
|
|
8
|
+
module Runners
|
|
9
|
+
module Instance
|
|
10
|
+
include Legion::Extensions::ServiceNow::Helpers::Client
|
|
11
|
+
|
|
12
|
+
def list_cis(class_name:, sysparm_limit: 100, sysparm_offset: 0, sysparm_query: nil, **)
|
|
13
|
+
params = { sysparm_limit: sysparm_limit, sysparm_offset: sysparm_offset }
|
|
14
|
+
params[:sysparm_query] = sysparm_query if sysparm_query
|
|
15
|
+
resp = connection(**).get("/api/now/cmdb/instance/#{class_name}", params)
|
|
16
|
+
{ records: resp.body['result']['records'] }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create_ci(class_name:, name: nil, ip_address: nil, os: nil, **)
|
|
20
|
+
body = {}
|
|
21
|
+
body[:name] = name if name
|
|
22
|
+
body[:ip_address] = ip_address if ip_address
|
|
23
|
+
body[:os] = os if os
|
|
24
|
+
resp = connection(**).post("/api/now/cmdb/instance/#{class_name}", body)
|
|
25
|
+
{ record: resp.body['result']['record'] }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def get_ci(class_name:, sys_id:, **)
|
|
29
|
+
resp = connection(**).get("/api/now/cmdb/instance/#{class_name}/#{sys_id}")
|
|
30
|
+
{ record: resp.body['result']['record'] }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def update_ci(class_name:, sys_id:, name: nil, ip_address: nil, os: nil, location: nil, **)
|
|
34
|
+
body = {}
|
|
35
|
+
body[:name] = name if name
|
|
36
|
+
body[:ip_address] = ip_address if ip_address
|
|
37
|
+
body[:os] = os if os
|
|
38
|
+
body[:location] = location if location
|
|
39
|
+
resp = connection(**).patch("/api/now/cmdb/instance/#{class_name}/#{sys_id}", body)
|
|
40
|
+
{ record: resp.body['result']['record'] }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def delete_ci(class_name:, sys_id:, **)
|
|
44
|
+
resp = connection(**).delete("/api/now/cmdb/instance/#{class_name}/#{sys_id}")
|
|
45
|
+
{ deleted: resp.status == 204, sys_id: sys_id }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def get_relationships(class_name:, sys_id:, **)
|
|
49
|
+
resp = connection(**).get("/api/now/cmdb/instance/#{class_name}/#{sys_id}/relationships")
|
|
50
|
+
{ relationships: resp.body['result'] }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def create_relationship(class_name:, sys_id:, target_id:, relationship_type:, **)
|
|
54
|
+
body = { target_id: target_id, relationship_type: relationship_type }
|
|
55
|
+
resp = connection(**).post("/api/now/cmdb/instance/#{class_name}/#{sys_id}/relationships", body)
|
|
56
|
+
{ relationship: resp.body['result'] }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
60
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
module Cmdb
|
|
7
|
+
module Meta
|
|
8
|
+
module Runners
|
|
9
|
+
module Meta
|
|
10
|
+
include Legion::Extensions::ServiceNow::Helpers::Client
|
|
11
|
+
|
|
12
|
+
def get_hierarchy(**)
|
|
13
|
+
resp = connection(**).get('/api/now/doc/meta/hierarchy')
|
|
14
|
+
{ hierarchy: resp.body['result'] }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def get_class_meta(class_name:, **)
|
|
18
|
+
resp = connection(**).get("/api/now/doc/meta/#{class_name}")
|
|
19
|
+
{ meta: resp.body['result'] }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
23
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module ServiceNow
|
|
8
|
+
module Helpers
|
|
9
|
+
module Client
|
|
10
|
+
def connection(url: nil, client_id: nil, client_secret: nil,
|
|
11
|
+
token: nil, username: nil, password: nil, **)
|
|
12
|
+
base_url = url || Legion::Settings[:service_now][:url]
|
|
13
|
+
Faraday.new(url: base_url) do |conn|
|
|
14
|
+
conn.request :json
|
|
15
|
+
conn.response :json, content_type: /\bjson$/
|
|
16
|
+
if client_id && client_secret
|
|
17
|
+
conn.headers['Authorization'] = "Bearer #{fetch_oauth2_token(base_url, client_id, client_secret)}"
|
|
18
|
+
elsif token
|
|
19
|
+
conn.headers['Authorization'] = "Bearer #{token}"
|
|
20
|
+
elsif username && password
|
|
21
|
+
conn.request :authorization, :basic, username, password
|
|
22
|
+
end
|
|
23
|
+
conn.adapter Faraday.default_adapter
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def fetch_oauth2_token(base_url, client_id, client_secret)
|
|
28
|
+
@fetch_oauth2_token ||= begin
|
|
29
|
+
resp = Faraday.new(url: base_url) do |conn|
|
|
30
|
+
conn.request :url_encoded
|
|
31
|
+
conn.response :json, content_type: /\bjson$/
|
|
32
|
+
conn.adapter Faraday.default_adapter
|
|
33
|
+
end.post('/oauth_token.do', {
|
|
34
|
+
grant_type: 'client_credentials',
|
|
35
|
+
client_id: client_id,
|
|
36
|
+
client_secret: client_secret
|
|
37
|
+
})
|
|
38
|
+
resp.body['access_token']
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
module Knowledge
|
|
7
|
+
module Runners
|
|
8
|
+
module Knowledge
|
|
9
|
+
include Legion::Extensions::ServiceNow::Helpers::Client
|
|
10
|
+
|
|
11
|
+
def list_articles(sysparm_limit: 100, sysparm_offset: 0, sysparm_query: nil, **)
|
|
12
|
+
params = { sysparm_limit: sysparm_limit, sysparm_offset: sysparm_offset }
|
|
13
|
+
params[:sysparm_query] = sysparm_query if sysparm_query
|
|
14
|
+
resp = connection(**).get('/api/sn_km_api/knowledge', params)
|
|
15
|
+
{ articles: resp.body['result'] }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def get_article(sys_id:, **)
|
|
19
|
+
resp = connection(**).get("/api/sn_km_api/knowledge/#{sys_id}")
|
|
20
|
+
{ article: resp.body['result'] }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def create_article(short_description:, text: nil, knowledge_base: nil, **)
|
|
24
|
+
body = { short_description: short_description }
|
|
25
|
+
body[:text] = text if text
|
|
26
|
+
body[:knowledge_base] = knowledge_base if knowledge_base
|
|
27
|
+
resp = connection(**).post('/api/sn_km_api/knowledge', body)
|
|
28
|
+
{ article: resp.body['result'] }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def update_article(sys_id:, short_description: nil, text: nil, **)
|
|
32
|
+
body = {}
|
|
33
|
+
body[:short_description] = short_description if short_description
|
|
34
|
+
body[:text] = text if text
|
|
35
|
+
resp = connection(**).patch("/api/sn_km_api/knowledge/#{sys_id}", body)
|
|
36
|
+
{ article: resp.body['result'] }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def delete_article(sys_id:, **)
|
|
40
|
+
resp = connection(**).delete("/api/sn_km_api/knowledge/#{sys_id}")
|
|
41
|
+
{ deleted: resp.status == 204, sys_id: sys_id }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
45
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
module ServiceCatalog
|
|
7
|
+
module Runners
|
|
8
|
+
module ServiceCatalog
|
|
9
|
+
include Legion::Extensions::ServiceNow::Helpers::Client
|
|
10
|
+
|
|
11
|
+
def list_catalogs(sysparm_limit: 100, sysparm_offset: 0, **)
|
|
12
|
+
params = { sysparm_limit: sysparm_limit, sysparm_offset: sysparm_offset }
|
|
13
|
+
resp = connection(**).get('/api/sn_sc/servicecatalog/catalogs', params)
|
|
14
|
+
{ catalogs: resp.body['result'] }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def get_catalog(sys_id:, **)
|
|
18
|
+
resp = connection(**).get("/api/sn_sc/servicecatalog/catalogs/#{sys_id}")
|
|
19
|
+
{ catalog: resp.body['result'] }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get_category(sys_id:, **)
|
|
23
|
+
resp = connection(**).get("/api/sn_sc/servicecatalog/categories/#{sys_id}")
|
|
24
|
+
{ category: resp.body['result'] }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def list_items(sysparm_limit: 100, sysparm_offset: 0, sysparm_query: nil,
|
|
28
|
+
catalog_id: nil, category_id: nil, **)
|
|
29
|
+
params = { sysparm_limit: sysparm_limit, sysparm_offset: sysparm_offset }
|
|
30
|
+
params[:sysparm_query] = sysparm_query if sysparm_query
|
|
31
|
+
params[:catalog_id] = catalog_id if catalog_id
|
|
32
|
+
params[:category_id] = category_id if category_id
|
|
33
|
+
resp = connection(**).get('/api/sn_sc/servicecatalog/items', params)
|
|
34
|
+
{ items: resp.body['result'] }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def get_item(sys_id:, **)
|
|
38
|
+
resp = connection(**).get("/api/sn_sc/servicecatalog/items/#{sys_id}")
|
|
39
|
+
{ item: resp.body['result'] }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def get_item_variables(sys_id:, **)
|
|
43
|
+
resp = connection(**).get("/api/sn_sc/servicecatalog/items/#{sys_id}/variables")
|
|
44
|
+
{ variables: resp.body['result'] }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def order_now(sys_id:, quantity: 1, variables: nil, **)
|
|
48
|
+
body = { quantity: quantity }
|
|
49
|
+
body[:variables] = variables if variables
|
|
50
|
+
resp = connection(**).post("/api/sn_sc/servicecatalog/items/#{sys_id}/order_now", body)
|
|
51
|
+
{ request: resp.body['result'] }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def add_to_cart(sys_id:, quantity: 1, variables: nil, **)
|
|
55
|
+
body = { quantity: quantity }
|
|
56
|
+
body[:variables] = variables if variables
|
|
57
|
+
resp = connection(**).post("/api/sn_sc/servicecatalog/items/#{sys_id}/add_to_cart", body)
|
|
58
|
+
{ cart: resp.body['result'] }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def get_cart(**)
|
|
62
|
+
resp = connection(**).get('/api/sn_sc/servicecatalog/cart')
|
|
63
|
+
{ cart: resp.body['result'] }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def checkout_cart(**)
|
|
67
|
+
resp = connection(**).post('/api/sn_sc/servicecatalog/cart/checkout', {})
|
|
68
|
+
{ order: resp.body['result'] }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def delete_cart(cart_id:, **)
|
|
72
|
+
resp = connection(**).delete("/api/sn_sc/servicecatalog/cart/#{cart_id}")
|
|
73
|
+
{ deleted: resp.status == 204, cart_id: cart_id }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
77
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
module Skills
|
|
7
|
+
class ChangeRequest < Legion::LLM::Skills::Base
|
|
8
|
+
skill_name 'servicenow:change_request'
|
|
9
|
+
namespace 'servicenow'
|
|
10
|
+
description 'Guides through ServiceNow change request creation and CAB approval workflow'
|
|
11
|
+
trigger :on_demand
|
|
12
|
+
trigger_words 'CHG', 'RFC', 'change request', 'CAB', 'change advisory board', 'change management'
|
|
13
|
+
|
|
14
|
+
steps :gather_requirements,
|
|
15
|
+
:risk_assessment,
|
|
16
|
+
:draft_and_review,
|
|
17
|
+
:submit_for_approval,
|
|
18
|
+
:complete
|
|
19
|
+
|
|
20
|
+
def gather_requirements(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
21
|
+
Legion::LLM::Skills::StepResult.new(
|
|
22
|
+
inject: 'Gathering change requirements: scope, timeline, and affected systems.',
|
|
23
|
+
metadata: { step: 'gather_requirements' }
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def risk_assessment(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
28
|
+
Legion::LLM::Skills::StepResult.new(
|
|
29
|
+
inject: 'Assessing risk: impact, likelihood, and rollback plan.',
|
|
30
|
+
metadata: { step: 'risk_assessment' }
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def draft_and_review(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
35
|
+
Legion::LLM::Skills::StepResult.new(
|
|
36
|
+
inject: 'Drafting change request. Please review the details.',
|
|
37
|
+
metadata: { step: 'draft_and_review' },
|
|
38
|
+
gate: :confirm
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def submit_for_approval(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
43
|
+
Legion::LLM::Skills::StepResult.new(
|
|
44
|
+
inject: 'Submitting change request for CAB approval.',
|
|
45
|
+
metadata: { step: 'submit_for_approval' }
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def complete(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
50
|
+
Legion::LLM::Skills::StepResult.new(
|
|
51
|
+
inject: 'Change request submitted successfully.',
|
|
52
|
+
metadata: { step: 'complete' }
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
module Skills
|
|
7
|
+
class CmdbQuery < Legion::LLM::Skills::Base
|
|
8
|
+
skill_name 'servicenow:cmdb_query'
|
|
9
|
+
namespace 'servicenow'
|
|
10
|
+
description 'Guides through CMDB configuration item lookup and relationship exploration'
|
|
11
|
+
trigger :on_demand
|
|
12
|
+
trigger_words 'CMDB', 'CI', 'configuration item', 'asset', 'cmdb_ci', 'server lookup'
|
|
13
|
+
|
|
14
|
+
steps :clarify_query,
|
|
15
|
+
:scope_search,
|
|
16
|
+
:present_results,
|
|
17
|
+
:complete
|
|
18
|
+
|
|
19
|
+
def clarify_query(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
20
|
+
Legion::LLM::Skills::StepResult.new(
|
|
21
|
+
inject: 'Clarifying CMDB query: what CI class and attributes are you looking for?',
|
|
22
|
+
metadata: { step: 'clarify_query' },
|
|
23
|
+
gate: :confirm
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def scope_search(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
28
|
+
Legion::LLM::Skills::StepResult.new(
|
|
29
|
+
inject: 'Scoping CMDB search by class, name, and environment.',
|
|
30
|
+
metadata: { step: 'scope_search' }
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def present_results(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
35
|
+
Legion::LLM::Skills::StepResult.new(
|
|
36
|
+
inject: 'Presenting CMDB query results with relationships.',
|
|
37
|
+
metadata: { step: 'present_results' }
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def complete(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
42
|
+
Legion::LLM::Skills::StepResult.new(
|
|
43
|
+
inject: 'CMDB query complete.',
|
|
44
|
+
metadata: { step: 'complete' }
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
module Skills
|
|
7
|
+
class Incident < Legion::LLM::Skills::Base
|
|
8
|
+
skill_name 'servicenow:incident'
|
|
9
|
+
namespace 'servicenow'
|
|
10
|
+
description 'Guides through ServiceNow incident response workflow'
|
|
11
|
+
trigger :on_demand
|
|
12
|
+
trigger_words 'incident', 'INC', 'outage', 'p1', 'sev1', 'production down', 'service down'
|
|
13
|
+
|
|
14
|
+
steps :gather_context,
|
|
15
|
+
:assess_severity,
|
|
16
|
+
:determine_action,
|
|
17
|
+
:confirm_with_user,
|
|
18
|
+
:complete
|
|
19
|
+
|
|
20
|
+
def gather_context(context: {})
|
|
21
|
+
Legion::LLM::Skills::StepResult.new(
|
|
22
|
+
inject: "Gathering incident context from ServiceNow. Project: #{detect_project(context)}",
|
|
23
|
+
metadata: { step: 'gather_context' }
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def assess_severity(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
28
|
+
Legion::LLM::Skills::StepResult.new(
|
|
29
|
+
inject: 'Assessing incident severity based on impact and urgency.',
|
|
30
|
+
metadata: { step: 'assess_severity' }
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def determine_action(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
35
|
+
Legion::LLM::Skills::StepResult.new(
|
|
36
|
+
inject: 'Determining recommended action: escalate, assign, or resolve.',
|
|
37
|
+
metadata: { step: 'determine_action' },
|
|
38
|
+
gate: :confirm
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def confirm_with_user(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
43
|
+
Legion::LLM::Skills::StepResult.new(
|
|
44
|
+
inject: 'Awaiting user confirmation before proceeding.',
|
|
45
|
+
metadata: { step: 'confirm_with_user' }
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def complete(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
50
|
+
Legion::LLM::Skills::StepResult.new(
|
|
51
|
+
inject: 'Incident workflow complete.',
|
|
52
|
+
metadata: { step: 'complete' }
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
module Skills
|
|
7
|
+
class Knowledge < Legion::LLM::Skills::Base
|
|
8
|
+
skill_name 'servicenow:knowledge'
|
|
9
|
+
namespace 'servicenow'
|
|
10
|
+
description 'Guides through ServiceNow knowledge base article search and retrieval'
|
|
11
|
+
trigger :on_demand
|
|
12
|
+
trigger_words 'KB', 'knowledge base', 'knowledge article', 'how to', 'documentation search'
|
|
13
|
+
|
|
14
|
+
steps :gather_search_terms,
|
|
15
|
+
:search_articles,
|
|
16
|
+
:present_results,
|
|
17
|
+
:complete
|
|
18
|
+
|
|
19
|
+
def gather_search_terms(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
20
|
+
Legion::LLM::Skills::StepResult.new(
|
|
21
|
+
inject: 'What are you looking for in the knowledge base?',
|
|
22
|
+
metadata: { step: 'gather_search_terms' },
|
|
23
|
+
gate: :confirm
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def search_articles(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
28
|
+
Legion::LLM::Skills::StepResult.new(
|
|
29
|
+
inject: 'Searching ServiceNow knowledge base.',
|
|
30
|
+
metadata: { step: 'search_articles' }
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def present_results(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
35
|
+
Legion::LLM::Skills::StepResult.new(
|
|
36
|
+
inject: 'Presenting matching knowledge articles.',
|
|
37
|
+
metadata: { step: 'present_results' }
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def complete(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
42
|
+
Legion::LLM::Skills::StepResult.new(
|
|
43
|
+
inject: 'Knowledge search complete.',
|
|
44
|
+
metadata: { step: 'complete' }
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ServiceNow
|
|
6
|
+
module Skills
|
|
7
|
+
class ServiceCatalog < Legion::LLM::Skills::Base
|
|
8
|
+
skill_name 'servicenow:service_catalog'
|
|
9
|
+
namespace 'servicenow'
|
|
10
|
+
description 'Guides through ServiceNow service catalog browsing and request ordering'
|
|
11
|
+
trigger :on_demand
|
|
12
|
+
trigger_words 'catalog', 'service request', 'order', 'request item', 'service catalog'
|
|
13
|
+
|
|
14
|
+
steps :browse_catalog,
|
|
15
|
+
:select_item,
|
|
16
|
+
:configure_request,
|
|
17
|
+
:submit_order,
|
|
18
|
+
:complete
|
|
19
|
+
|
|
20
|
+
def browse_catalog(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
21
|
+
Legion::LLM::Skills::StepResult.new(
|
|
22
|
+
inject: 'Browsing ServiceNow service catalog.',
|
|
23
|
+
metadata: { step: 'browse_catalog' }
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def select_item(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
28
|
+
Legion::LLM::Skills::StepResult.new(
|
|
29
|
+
inject: 'Which catalog item would you like to request?',
|
|
30
|
+
metadata: { step: 'select_item' },
|
|
31
|
+
gate: :confirm
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def configure_request(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
36
|
+
Legion::LLM::Skills::StepResult.new(
|
|
37
|
+
inject: 'Configuring request variables and quantity.',
|
|
38
|
+
metadata: { step: 'configure_request' },
|
|
39
|
+
gate: :confirm
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def submit_order(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
44
|
+
Legion::LLM::Skills::StepResult.new(
|
|
45
|
+
inject: 'Submitting service catalog order.',
|
|
46
|
+
metadata: { step: 'submit_order' }
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def complete(context: {}) # rubocop:disable Lint/UnusedMethodArgument
|
|
51
|
+
Legion::LLM::Skills::StepResult.new(
|
|
52
|
+
inject: 'Service catalog request submitted.',
|
|
53
|
+
metadata: { step: 'complete' }
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/service_now/version'
|
|
4
|
+
require 'legion/extensions/service_now/helpers/client'
|
|
5
|
+
|
|
6
|
+
require 'legion/extensions/service_now/change/runners/change'
|
|
7
|
+
require 'legion/extensions/service_now/cmdb/instance/runners/instance'
|
|
8
|
+
require 'legion/extensions/service_now/cmdb/meta/runners/meta'
|
|
9
|
+
require 'legion/extensions/service_now/knowledge/runners/knowledge'
|
|
10
|
+
require 'legion/extensions/service_now/service_catalog/runners/service_catalog'
|
|
11
|
+
require 'legion/extensions/service_now/account/runners/account'
|
|
12
|
+
|
|
13
|
+
require 'legion/extensions/service_now/client'
|
|
14
|
+
|
|
15
|
+
if defined?(Legion::LLM)
|
|
16
|
+
require 'legion/extensions/service_now/skills/incident'
|
|
17
|
+
require 'legion/extensions/service_now/skills/change_request'
|
|
18
|
+
require 'legion/extensions/service_now/skills/cmdb_query'
|
|
19
|
+
require 'legion/extensions/service_now/skills/knowledge'
|
|
20
|
+
require 'legion/extensions/service_now/skills/service_catalog'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module Legion
|
|
24
|
+
module Extensions
|
|
25
|
+
module ServiceNow
|
|
26
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-service_now
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Esity
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: faraday
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: legion-cache
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 1.3.11
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 1.3.11
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: legion-crypt
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 1.4.9
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 1.4.9
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: legion-data
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 1.4.17
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 1.4.17
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: legion-json
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 1.2.1
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 1.2.1
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: legion-logging
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: 1.3.2
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 1.3.2
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: legion-settings
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 1.3.14
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 1.3.14
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: legion-transport
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: 1.3.9
|
|
117
|
+
type: :runtime
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: 1.3.9
|
|
124
|
+
description: Connects Legion to ServiceNow via Change Management, CMDB, Knowledge,
|
|
125
|
+
Service Catalog, and Account REST APIs
|
|
126
|
+
email:
|
|
127
|
+
- matthewdiverson@gmail.com
|
|
128
|
+
executables: []
|
|
129
|
+
extensions: []
|
|
130
|
+
extra_rdoc_files: []
|
|
131
|
+
files:
|
|
132
|
+
- ".github/CODEOWNERS"
|
|
133
|
+
- ".github/dependabot.yml"
|
|
134
|
+
- ".github/workflows/ci.yml"
|
|
135
|
+
- ".gitignore"
|
|
136
|
+
- ".rspec"
|
|
137
|
+
- ".rubocop.yml"
|
|
138
|
+
- CHANGELOG.md
|
|
139
|
+
- Gemfile
|
|
140
|
+
- LICENSE
|
|
141
|
+
- Rakefile
|
|
142
|
+
- lex-service_now.gemspec
|
|
143
|
+
- lib/legion/extensions/service_now.rb
|
|
144
|
+
- lib/legion/extensions/service_now/account/runners/account.rb
|
|
145
|
+
- lib/legion/extensions/service_now/change/runners/change.rb
|
|
146
|
+
- lib/legion/extensions/service_now/client.rb
|
|
147
|
+
- lib/legion/extensions/service_now/cmdb/instance/runners/instance.rb
|
|
148
|
+
- lib/legion/extensions/service_now/cmdb/meta/runners/meta.rb
|
|
149
|
+
- lib/legion/extensions/service_now/helpers/client.rb
|
|
150
|
+
- lib/legion/extensions/service_now/knowledge/runners/knowledge.rb
|
|
151
|
+
- lib/legion/extensions/service_now/service_catalog/runners/service_catalog.rb
|
|
152
|
+
- lib/legion/extensions/service_now/skills/change_request.rb
|
|
153
|
+
- lib/legion/extensions/service_now/skills/cmdb_query.rb
|
|
154
|
+
- lib/legion/extensions/service_now/skills/incident.rb
|
|
155
|
+
- lib/legion/extensions/service_now/skills/knowledge.rb
|
|
156
|
+
- lib/legion/extensions/service_now/skills/service_catalog.rb
|
|
157
|
+
- lib/legion/extensions/service_now/version.rb
|
|
158
|
+
homepage: https://github.com/LegionIO/lex-service_now
|
|
159
|
+
licenses:
|
|
160
|
+
- MIT
|
|
161
|
+
metadata:
|
|
162
|
+
homepage_uri: https://github.com/LegionIO/lex-service_now
|
|
163
|
+
source_code_uri: https://github.com/LegionIO/lex-service_now
|
|
164
|
+
changelog_uri: https://github.com/LegionIO/lex-service_now/blob/main/CHANGELOG.md
|
|
165
|
+
rubygems_mfa_required: 'true'
|
|
166
|
+
rdoc_options: []
|
|
167
|
+
require_paths:
|
|
168
|
+
- lib
|
|
169
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '3.4'
|
|
174
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
|
+
requirements:
|
|
176
|
+
- - ">="
|
|
177
|
+
- !ruby/object:Gem::Version
|
|
178
|
+
version: '0'
|
|
179
|
+
requirements: []
|
|
180
|
+
rubygems_version: 3.6.9
|
|
181
|
+
specification_version: 4
|
|
182
|
+
summary: LEX::ServiceNow
|
|
183
|
+
test_files: []
|