foreman_teamdynamix 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +99 -0
- data/Rakefile +47 -0
- data/app/controllers/concerns/foreman_teamdynamix/hosts_controller_extensions.rb +23 -0
- data/app/helpers/concerns/foreman_teamdynamix/hosts_helper_extensions.rb +57 -0
- data/app/models/concerns/foreman_teamdynamix/host_extensions.rb +32 -0
- data/app/overrides/add_tab.rb +7 -0
- data/app/overrides/add_tab_link.rb +10 -0
- data/app/services/teamdynamix_api.rb +150 -0
- data/app/views/foreman_teamdynamix/hosts/_teamdynamix.html.erb +15 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20171228033228_add_teamdynamix_asset_id_to_hosts.foreman_teamdynamix.rb +5 -0
- data/lib/foreman_teamdynamix/engine.rb +50 -0
- data/lib/foreman_teamdynamix/version.rb +3 -0
- data/lib/foreman_teamdynamix.rb +4 -0
- data/lib/tasks/foreman_teamdynamix_tasks.rake +40 -0
- data/lib/tasks/sync_hosts_with_teamdynamix.rake +96 -0
- data/locale/Makefile +60 -0
- data/locale/en/foreman_teamdynamix.po +18 -0
- data/locale/foreman_teamdynamix.pot +18 -0
- data/locale/gemspec.rb +2 -0
- data/test/fake_teamdynamix_api.rb +17 -0
- data/test/functional/concerns/hosts_controller_extensions_test.rb +31 -0
- data/test/helpers/concerns/foreman_teamdynamix/hosts_helper_extensions_test.rb +63 -0
- data/test/models/host_extensions_test.rb +37 -0
- data/test/sample_asset.json +193 -0
- data/test/services/teamdynamix_api_test.rb +165 -0
- data/test/test_plugin_helper.rb +38 -0
- data/test/unit/foreman_teamdynamix_test.rb +11 -0
- metadata +124 -0
data/locale/Makefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#
|
2
|
+
# Makefile for PO merging and MO generation. More info in the README.
|
3
|
+
#
|
4
|
+
# make all-mo (default) - generate MO files
|
5
|
+
# make check - check translations using translate-tool
|
6
|
+
# make tx-update - download and merge translations from Transifex
|
7
|
+
# make clean - clean everything
|
8
|
+
#
|
9
|
+
DOMAIN = foreman_teamdynamix
|
10
|
+
VERSION = $(shell ruby -e 'require "rubygems";spec = Gem::Specification::load(Dir.glob("../*.gemspec")[0]);puts spec.version')
|
11
|
+
POTFILE = $(DOMAIN).pot
|
12
|
+
MOFILE = $(DOMAIN).mo
|
13
|
+
POFILES = $(shell find . -name '$(DOMAIN).po')
|
14
|
+
MOFILES = $(patsubst %.po,%.mo,$(POFILES))
|
15
|
+
POXFILES = $(patsubst %.po,%.pox,$(POFILES))
|
16
|
+
EDITFILES = $(patsubst %.po,%.edit.po,$(POFILES))
|
17
|
+
|
18
|
+
%.mo: %.po
|
19
|
+
mkdir -p $(shell dirname $@)/LC_MESSAGES
|
20
|
+
msgfmt -o $(shell dirname $@)/LC_MESSAGES/$(MOFILE) $<
|
21
|
+
|
22
|
+
# Generate MO files from PO files
|
23
|
+
all-mo: $(MOFILES)
|
24
|
+
|
25
|
+
# Check for malformed strings
|
26
|
+
%.pox: %.po
|
27
|
+
msgfmt -c $<
|
28
|
+
pofilter --nofuzzy -t variables -t blank -t urls -t emails -t long -t newlines \
|
29
|
+
-t endwhitespace -t endpunc -t puncspacing -t options -t printf -t validchars --gnome $< > $@
|
30
|
+
cat $@
|
31
|
+
! grep -q msgid $@
|
32
|
+
|
33
|
+
%.edit.po:
|
34
|
+
touch $@
|
35
|
+
|
36
|
+
check: $(POXFILES)
|
37
|
+
|
38
|
+
# Unify duplicate translations
|
39
|
+
uniq-po:
|
40
|
+
for f in $(shell find ./ -name "*.po") ; do \
|
41
|
+
msguniq $$f -o $$f ; \
|
42
|
+
done
|
43
|
+
|
44
|
+
tx-pull: $(EDITFILES)
|
45
|
+
tx pull -f
|
46
|
+
for f in $(EDITFILES) ; do \
|
47
|
+
sed -i 's/^\("Project-Id-Version: \).*$$/\1$(DOMAIN) $(VERSION)\\n"/' $$f; \
|
48
|
+
done
|
49
|
+
|
50
|
+
tx-update: tx-pull
|
51
|
+
@echo
|
52
|
+
@echo Run rake plugin:gettext[$(DOMAIN)] from the Foreman installation, then make -C locale mo-files to finish
|
53
|
+
@echo
|
54
|
+
|
55
|
+
mo-files: $(MOFILES)
|
56
|
+
git add $(POFILES) $(POTFILE) ../locale/*/LC_MESSAGES
|
57
|
+
git commit -m "i18n - pulling from tx"
|
58
|
+
@echo
|
59
|
+
@echo Changes commited!
|
60
|
+
@echo
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# foreman_teamdynamix
|
2
|
+
#
|
3
|
+
# This file is distributed under the same license as foreman_teamdynamix.
|
4
|
+
#
|
5
|
+
#, fuzzy
|
6
|
+
msgid ""
|
7
|
+
msgstr ""
|
8
|
+
"Project-Id-Version: version 0.0.1\n"
|
9
|
+
"Report-Msgid-Bugs-To: \n"
|
10
|
+
"POT-Creation-Date: 2014-08-20 08:46+0100\n"
|
11
|
+
"PO-Revision-Date: 2014-08-20 08:54+0100\n"
|
12
|
+
"Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
|
13
|
+
"Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
|
14
|
+
"Language: \n"
|
15
|
+
"MIME-Version: 1.0\n"
|
16
|
+
"Content-Type: text/plain; charset=UTF-8\n"
|
17
|
+
"Content-Transfer-Encoding: 8bit\n"
|
18
|
+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# foreman_teamdynamix
|
2
|
+
#
|
3
|
+
# This file is distributed under the same license as foreman_teamdynamix.
|
4
|
+
#
|
5
|
+
#, fuzzy
|
6
|
+
msgid ""
|
7
|
+
msgstr ""
|
8
|
+
"Project-Id-Version: version 0.0.1\n"
|
9
|
+
"Report-Msgid-Bugs-To: \n"
|
10
|
+
"POT-Creation-Date: 2014-08-20 08:46+0100\n"
|
11
|
+
"PO-Revision-Date: 2014-08-20 08:46+0100\n"
|
12
|
+
"Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
|
13
|
+
"Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
|
14
|
+
"Language: \n"
|
15
|
+
"MIME-Version: 1.0\n"
|
16
|
+
"Content-Type: text/plain; charset=UTF-8\n"
|
17
|
+
"Content-Transfer-Encoding: 8bit\n"
|
18
|
+
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
data/locale/gemspec.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class FakeTeamdynamixApi
|
2
|
+
def create_asset(*)
|
3
|
+
get_asset
|
4
|
+
end
|
5
|
+
|
6
|
+
def search_asset(*)
|
7
|
+
Array.wrap(get_asset)
|
8
|
+
end
|
9
|
+
|
10
|
+
def retire_asset(*)
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_asset(*)
|
15
|
+
JSON.parse(File.read(File.join(File.dirname(__FILE__), 'sample_asset.json')))
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
class HostsControllerTest < ActionController::TestCase
|
4
|
+
let(:td_tab_title) { SETTINGS[:teamdynamix][:title] || 'Team Dynamix' }
|
5
|
+
let(:host) { FactoryBot.create(:host, :managed) }
|
6
|
+
let(:td_api) { FakeTeamdynamixApi.new }
|
7
|
+
before do
|
8
|
+
Host::Managed.any_instance.stubs(:td_api).returns(td_api)
|
9
|
+
end
|
10
|
+
# rubocop:disable Style/StringLiterals, HttpPositionalArguments
|
11
|
+
describe 'Given host exist as an asset in TeamDynamix' do
|
12
|
+
describe 'when TeamDynamix asset attributes are configured' do
|
13
|
+
describe 'GET hosts/show' do
|
14
|
+
test 'loads the TeamDynamix tab' do
|
15
|
+
get :show, { :id => host.name }, set_session_user
|
16
|
+
assert_includes response.headers['Content-Type'], 'text/html'
|
17
|
+
assert_includes response.body, "<ul id=\"myTab\""
|
18
|
+
assert_equal response.status, 200
|
19
|
+
assert_includes response.body, "<li><a href=\"#teamdynamix\" data-toggle=\"tab\">#{td_tab_title}</a></li>"
|
20
|
+
assert_includes response.body, "<div id=\"teamdynamix\" class=\"tab-pane\" data-ajax-url=\"/hosts/#{host.name}/teamdynamix\" data-on-complete=\"onContentLoad\">"
|
21
|
+
end
|
22
|
+
test 'TeamDynamix tab contains configured asset attributes' do
|
23
|
+
skip
|
24
|
+
get hosts_teamdynamix_path, { :id => host.name }, set_session_user
|
25
|
+
assert_template 'foreman_teamdynamix'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
# rubocop:enable Style/StringLiterals, HttpPositionalArguments
|
31
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
class HostsHelperExtensionsTest < ActiveSupport::TestCase
|
4
|
+
include ActionView::Helpers::UrlHelper
|
5
|
+
include ForemanTeamdynamix::HostsHelperExtensions
|
6
|
+
let(:host) { FactoryBot.create(:host, :managed) }
|
7
|
+
let(:td_api) { FakeTeamdynamixApi.new }
|
8
|
+
before do
|
9
|
+
Host::Managed.any_instance.stubs(:td_api).returns(td_api)
|
10
|
+
@host = host
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#teamdynamix_fields' do
|
14
|
+
let(:sample_asset) { td_api.get_asset }
|
15
|
+
let(:default_fields) { [sample_asset_uri] }
|
16
|
+
let(:direct_attribs_config) { { 'Asset ID' => 'ID', 'Owner' => 'OwningCustomerName', 'Parent Asset' => 'ParentID' } }
|
17
|
+
let(:direct_attribs_fields) { get_direct_asset_attribs_val(direct_attribs_config) }
|
18
|
+
let(:expected_fields) { default_fields + direct_attribs_fields }
|
19
|
+
before do
|
20
|
+
SETTINGS[:teamdynamix][:fields] = {}
|
21
|
+
SETTINGS[:teamdynamix][:fields].merge!(direct_attribs_config)
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'configuration only has attributes' do
|
25
|
+
test 'returns fields as expected' do
|
26
|
+
assert_equal teamdynamix_fields, expected_fields
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'configuration has nested attributes' do
|
31
|
+
let(:nested_attribs_config) do
|
32
|
+
{ 'mu.ci.Description' => "Attributes.'mu.ci.Description'",
|
33
|
+
'Ticket Routing Details' => "Attributes.'Ticket Routing Details'" }
|
34
|
+
end
|
35
|
+
let(:nested_attribs_fields) { get_nested_asset_attribs_val(nested_attribs_config) }
|
36
|
+
let(:expected_fields) { default_fields + nested_attribs_fields }
|
37
|
+
before do
|
38
|
+
SETTINGS[:teamdynamix][:fields] = {}
|
39
|
+
SETTINGS[:teamdynamix][:fields].merge!(nested_attribs_config)
|
40
|
+
end
|
41
|
+
test 'returns fields as expected' do
|
42
|
+
assert_equal teamdynamix_fields, expected_fields
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#teamdynamix_title' do
|
48
|
+
let(:title_orig) { SETTINGS[:teamdynamix][:title] }
|
49
|
+
before do
|
50
|
+
title_orig
|
51
|
+
SETTINGS[:teamdynamix][:title] = 'TeamDynamix Tab'
|
52
|
+
end
|
53
|
+
test 'returns correct title' do
|
54
|
+
assert_equal teamdynamix_title, SETTINGS[:teamdynamix][:title]
|
55
|
+
end
|
56
|
+
|
57
|
+
test 'settings title is not present: return default title' do
|
58
|
+
SETTINGS[:teamdynamix][:title] = nil
|
59
|
+
assert_equal teamdynamix_title, 'Team Dynamix'
|
60
|
+
SETTINGS[:teamdynamix][:title] = title_orig
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class HostExtensionsTests < ActiveSupport::TestCase
|
4
|
+
let(:td_api) { FakeTeamdynamixApi.new }
|
5
|
+
before do
|
6
|
+
Host::Managed.any_instance.stubs(:td_api).returns(td_api)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#create' do
|
10
|
+
let(:host) { FactoryBot.create(:host, :managed) }
|
11
|
+
it 'triggers after_create callback on Host::Managed model' do
|
12
|
+
assert_send([Host::Managed, :after_validation, :create_teamdynamix_asset])
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'calls Teamdynamix API to create an asset' do
|
16
|
+
assert_send([td_api, :create_asset, host])
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'sets host#teamdynamix_asset_id' do
|
20
|
+
assert_not_nil(host.teamdynamix_asset_id)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#destroy' do
|
25
|
+
let(:host) { FactoryBot.create(:host, :managed) }
|
26
|
+
before do
|
27
|
+
host.destroy
|
28
|
+
end
|
29
|
+
it 'triggers before_destroy callback' do
|
30
|
+
assert_send([Host::Managed, :before_destroy, :retire_teamdynamix_asset])
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'calls Teamdynamix API to retire an asset' do
|
34
|
+
assert_send([td_api, :retire_asset, host.teamdynamix_asset_id])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
{
|
2
|
+
"ID": 1111,
|
3
|
+
"AppID": 111,
|
4
|
+
"AppName": "Assets/CIs",
|
5
|
+
"ProductModelID": 1070,
|
6
|
+
"ProductModelName": "Application/Software",
|
7
|
+
"ManufacturerID": 1030,
|
8
|
+
"ManufacturerName": "MU",
|
9
|
+
"SupplierID": 1031,
|
10
|
+
"SupplierName": "Supplier",
|
11
|
+
"StatusID": 641,
|
12
|
+
"StatusName": "In Use",
|
13
|
+
"LocationID": 0,
|
14
|
+
"LocationName": "None",
|
15
|
+
"LocationRoomID": 0,
|
16
|
+
"LocationRoomName": "None",
|
17
|
+
"Tag": null,
|
18
|
+
"SerialNumber": "delete.foreman_teamdynamix.com",
|
19
|
+
"Name": "delete.foreman_teamdynamix.com",
|
20
|
+
"PurchaseCost": 0,
|
21
|
+
"AcquisitionDate": "0001-01-01T05:00:00Z",
|
22
|
+
"ExpectedReplacementDate": "0001-01-01T05:00:00Z",
|
23
|
+
"RequestingCustomerID": "00000000-0000-0000-0000-000000000000",
|
24
|
+
"RequestingCustomerName": "None",
|
25
|
+
"RequestingDepartmentID": 0,
|
26
|
+
"RequestingDepartmentName": "None",
|
27
|
+
"OwningCustomerID": "00000000-0000-0000-0000-000000000000",
|
28
|
+
"OwningCustomerName": "some name",
|
29
|
+
"OwningDepartmentID": 16253,
|
30
|
+
"OwningDepartmentName": "Advancement Services",
|
31
|
+
"ParentID": 10306,
|
32
|
+
"ParentSerialNumber": "Banner",
|
33
|
+
"ParentName": "",
|
34
|
+
"ParentTag": null,
|
35
|
+
"MaintenanceScheduleID": 0,
|
36
|
+
"MaintenanceScheduleName": "None",
|
37
|
+
"ConfigurationItemID": 16308,
|
38
|
+
"CreatedDate": "2014-11-20T21:22:52.057Z",
|
39
|
+
"CreatedUid": "00000000-0000-0000-0000-000000000000",
|
40
|
+
"CreatedFullName": "some name",
|
41
|
+
"ModifiedDate": "2017-09-15T15:25:09.79Z",
|
42
|
+
"ModifiedUid": "00000000-0000-0000-0000-000000000000",
|
43
|
+
"ModifiedFullName": "some name",
|
44
|
+
"ExternalID": "Banner Advancement",
|
45
|
+
"ExternalSourceID": 0,
|
46
|
+
"ExternalSourceName": "None",
|
47
|
+
"Attributes": [
|
48
|
+
{
|
49
|
+
"ID": 11636,
|
50
|
+
"Name": "Ticket Routing Details",
|
51
|
+
"Order": 0,
|
52
|
+
"Description": "",
|
53
|
+
"SectionID": 0,
|
54
|
+
"SectionName": null,
|
55
|
+
"FieldType": "textarea",
|
56
|
+
"DataType": "String",
|
57
|
+
"Choices": [],
|
58
|
+
"IsRequired": false,
|
59
|
+
"IsUpdatable": true,
|
60
|
+
"Value": "sample",
|
61
|
+
"ValueText": "",
|
62
|
+
"ChoicesText": "",
|
63
|
+
"AssociatedItemIDs": [
|
64
|
+
0
|
65
|
+
]
|
66
|
+
},
|
67
|
+
{
|
68
|
+
"ID": 11632,
|
69
|
+
"Name": "mu.ci.Description",
|
70
|
+
"Order": 50,
|
71
|
+
"Description": "",
|
72
|
+
"SectionID": 0,
|
73
|
+
"SectionName": null,
|
74
|
+
"FieldType": "textarea",
|
75
|
+
"DataType": "String",
|
76
|
+
"Choices": [],
|
77
|
+
"IsRequired": false,
|
78
|
+
"IsUpdatable": false,
|
79
|
+
"Value": "Foreman host delete.foreman_teamdynamix.com created by ForemanTeamdynamix plugin",
|
80
|
+
"ValueText": "",
|
81
|
+
"ChoicesText": "",
|
82
|
+
"AssociatedItemIDs": [
|
83
|
+
0
|
84
|
+
]
|
85
|
+
},
|
86
|
+
{
|
87
|
+
"ID": 11634,
|
88
|
+
"Name": "mu.ci.Lifecycle Status",
|
89
|
+
"Order": 50,
|
90
|
+
"Description": "This is the ITSM Lifecycle phase",
|
91
|
+
"SectionID": 0,
|
92
|
+
"SectionName": null,
|
93
|
+
"FieldType": "dropdown",
|
94
|
+
"DataType": "String",
|
95
|
+
"Choices": [
|
96
|
+
{
|
97
|
+
"ID": 26191,
|
98
|
+
"Name": "Development",
|
99
|
+
"IsActive": true,
|
100
|
+
"DateCreated": "2014-11-13T19:56:04.747Z",
|
101
|
+
"DateModified": "2014-11-13T19:56:04.747Z",
|
102
|
+
"Order": 0
|
103
|
+
},
|
104
|
+
{
|
105
|
+
"ID": 26192,
|
106
|
+
"Name": "Early-life Support",
|
107
|
+
"IsActive": true,
|
108
|
+
"DateCreated": "2014-11-13T19:56:04.747Z",
|
109
|
+
"DateModified": "2014-11-13T19:56:04.747Z",
|
110
|
+
"Order": 0
|
111
|
+
},
|
112
|
+
{
|
113
|
+
"ID": 26194,
|
114
|
+
"Name": "Pre-production",
|
115
|
+
"IsActive": true,
|
116
|
+
"DateCreated": "2014-11-13T19:56:04.75Z",
|
117
|
+
"DateModified": "2014-11-13T19:56:04.75Z",
|
118
|
+
"Order": 0
|
119
|
+
},
|
120
|
+
{
|
121
|
+
"ID": 26193,
|
122
|
+
"Name": "Production",
|
123
|
+
"IsActive": true,
|
124
|
+
"DateCreated": "2014-11-13T19:56:04.747Z",
|
125
|
+
"DateModified": "2014-11-13T19:56:04.747Z",
|
126
|
+
"Order": 0
|
127
|
+
},
|
128
|
+
{
|
129
|
+
"ID": 26190,
|
130
|
+
"Name": "Test",
|
131
|
+
"IsActive": true,
|
132
|
+
"DateCreated": "2014-11-13T19:56:04.747Z",
|
133
|
+
"DateModified": "2014-11-13T19:56:04.747Z",
|
134
|
+
"Order": 0
|
135
|
+
}
|
136
|
+
],
|
137
|
+
"IsRequired": true,
|
138
|
+
"IsUpdatable": false,
|
139
|
+
"Value": "26190",
|
140
|
+
"ValueText": "Production",
|
141
|
+
"ChoicesText": "Production",
|
142
|
+
"AssociatedItemIDs": [
|
143
|
+
0
|
144
|
+
]
|
145
|
+
},
|
146
|
+
{
|
147
|
+
"ID": 11639,
|
148
|
+
"Name": "mu.application.location",
|
149
|
+
"Order": 204,
|
150
|
+
"Description": "Where is the application running?",
|
151
|
+
"SectionID": 0,
|
152
|
+
"SectionName": null,
|
153
|
+
"FieldType": "hradio",
|
154
|
+
"DataType": "String",
|
155
|
+
"Choices": [
|
156
|
+
{
|
157
|
+
"ID": 26223,
|
158
|
+
"Name": "Cloud",
|
159
|
+
"IsActive": true,
|
160
|
+
"DateCreated": "2014-11-13T19:56:04.787Z",
|
161
|
+
"DateModified": "2014-11-13T19:56:04.787Z",
|
162
|
+
"Order": 0
|
163
|
+
},
|
164
|
+
{
|
165
|
+
"ID": 26224,
|
166
|
+
"Name": "Endpoint Device",
|
167
|
+
"IsActive": true,
|
168
|
+
"DateCreated": "2014-11-13T19:56:04.787Z",
|
169
|
+
"DateModified": "2014-11-13T19:56:04.787Z",
|
170
|
+
"Order": 0
|
171
|
+
},
|
172
|
+
{
|
173
|
+
"ID": 26222,
|
174
|
+
"Name": "Premise",
|
175
|
+
"IsActive": true,
|
176
|
+
"DateCreated": "2014-11-13T19:56:04.787Z",
|
177
|
+
"DateModified": "2014-11-13T19:56:04.787Z",
|
178
|
+
"Order": 0
|
179
|
+
}
|
180
|
+
],
|
181
|
+
"IsRequired": false,
|
182
|
+
"IsUpdatable": false,
|
183
|
+
"Value": "26222",
|
184
|
+
"ValueText": "Premise",
|
185
|
+
"ChoicesText": "Premise",
|
186
|
+
"AssociatedItemIDs": [
|
187
|
+
0
|
188
|
+
]
|
189
|
+
}
|
190
|
+
],
|
191
|
+
"Attachments": [],
|
192
|
+
"Uri": "api/730/assets/1111"
|
193
|
+
}
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
# rubocop:disable Metrics/ClassLength
|
3
|
+
class TeamdynamixApiTest < ActiveSupport::TestCase
|
4
|
+
# rubocop:enable Metrics/ClassLength
|
5
|
+
let(:subject) { TeamdynamixApi.instance }
|
6
|
+
let(:api_config) { SETTINGS[:teamdynamix][:api] }
|
7
|
+
let(:app_id) { api_config[:appId].to_s }
|
8
|
+
let(:api_url) { api_config[:url] }
|
9
|
+
let(:host) { FactoryBot.build(:host, :managed) }
|
10
|
+
let(:auth_payload) { { username: api_config[:username], password: api_config[:password] }.to_json }
|
11
|
+
let(:dummy_token) { 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6InR5YWdpbkBtaWFtaW9oLmVkdSIsImlzcyI6IlREIiwiYXVkIjoiaHR0cHM6Ly93d3cudGVhbWR5bmFtaXguY29tLyIsImV4cCI6MTUxNzA2OTU1OSwibmJmIjoxNTE2OTgzMTU5fQ.PkvKbYQCV-hY7_ni4-Zg3qJARBagSzz99fclBYyxxas' }
|
12
|
+
let(:sample_asset) { FakeTeamdynamixApi.new.get_asset }
|
13
|
+
let(:sample_asset_id) { sample_asset['ID'].to_s }
|
14
|
+
let(:host_name) { 'delete.foreman_teamdynamix.com' }
|
15
|
+
let(:get_asset_path) { api_url + '/' + app_id + '/assets/' + sample_asset_id }
|
16
|
+
let(:create_status_id) { 641 }
|
17
|
+
let(:custom_attributes) do
|
18
|
+
[{ 'name' => 'mu.ci.Lifecycle Status', 'id' => 11_634, 'value' => '26193' },
|
19
|
+
{ 'name' => 'mu.ci.Description', 'id' => 11_632, 'value' => 'Foreman host created by ForemanTeamdynamix plugin' }]
|
20
|
+
end
|
21
|
+
let(:create_path) { api_url + '/' + app_id + '/assets' }
|
22
|
+
let(:create_payload) do
|
23
|
+
{ AppID: app_id,
|
24
|
+
SerialNumber: host_name,
|
25
|
+
Name: host_name,
|
26
|
+
StatusID: create_status_id,
|
27
|
+
Attributes: custom_attributes }
|
28
|
+
end
|
29
|
+
before do
|
30
|
+
stub_request(:post, api_url + '/auth')
|
31
|
+
.with(body: auth_payload,
|
32
|
+
headers: { 'Content-Type' => 'application/json' })
|
33
|
+
.to_return(status: 200, body: dummy_token)
|
34
|
+
host.name = host_name
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#update_asset' do
|
38
|
+
let(:update_path) { get_asset_path }
|
39
|
+
let(:update_payload) { { ID: host.teamdynamix_asset_id }.merge!(create_payload) }
|
40
|
+
before do
|
41
|
+
host.teamdynamix_asset_id = sample_asset_id
|
42
|
+
SETTINGS[:teamdynamix][:api][:create] = { StatusID: create_status_id,
|
43
|
+
Attributes: custom_attributes }
|
44
|
+
end
|
45
|
+
context 'Valid Request' do
|
46
|
+
before do
|
47
|
+
stub_request(:post, update_path)
|
48
|
+
.with(headers: { 'Authorization' => 'Bearer ' + dummy_token,
|
49
|
+
'Content-Type' => 'application/json' },
|
50
|
+
body: update_payload)
|
51
|
+
.to_return(status: 200, body: sample_asset.to_json)
|
52
|
+
end
|
53
|
+
it 'successfully creates an asset and return it' do
|
54
|
+
asset = subject.update_asset(host)
|
55
|
+
assert_equal(asset['SerialNumber'], host.name)
|
56
|
+
assert_equal(asset['AppID'].to_s, app_id.to_s)
|
57
|
+
assert_equal(asset['StatusID'], create_status_id)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#retire_asset' do
|
63
|
+
let(:retire_status_id) { 642 }
|
64
|
+
before do
|
65
|
+
subject.stubs(:get_asset).returns(sample_asset)
|
66
|
+
SETTINGS[:teamdynamix][:api][:delete] = { StatusID: retire_status_id }
|
67
|
+
end
|
68
|
+
describe 'valid request' do
|
69
|
+
let(:retired_asset) { sample_asset.merge('StatusID' => retire_status_id) }
|
70
|
+
let(:retire_path) { api_url + '/' + app_id + '/assets/' + sample_asset_id }
|
71
|
+
before do
|
72
|
+
stub_request(:post, retire_path)
|
73
|
+
.with(headers: { 'Authorization' => 'Bearer ' + dummy_token,
|
74
|
+
'Content-Type' => 'application/json' })
|
75
|
+
.to_return(status: 200, body: retired_asset.to_json)
|
76
|
+
end
|
77
|
+
it 'marks the asset retired in Team Dynamix' do
|
78
|
+
assert_nothing_raised do
|
79
|
+
asset = subject.retire_asset(sample_asset_id)
|
80
|
+
assert_equal(asset['StatusID'], retire_status_id)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#create_asset' do
|
87
|
+
before do
|
88
|
+
SETTINGS[:teamdynamix][:api][:create] = { StatusID: create_status_id,
|
89
|
+
Attributes: custom_attributes }
|
90
|
+
end
|
91
|
+
context 'Valid Request' do
|
92
|
+
before do
|
93
|
+
stub_request(:post, create_path)
|
94
|
+
.with(headers: { 'Authorization' => 'Bearer ' + dummy_token,
|
95
|
+
'Content-Type' => 'application/json' },
|
96
|
+
body: create_payload)
|
97
|
+
.to_return(status: 200, body: sample_asset.to_json)
|
98
|
+
end
|
99
|
+
it 'successfully creates an asset and return it' do
|
100
|
+
asset = subject.create_asset(host)
|
101
|
+
assert_not_nil(asset['ID'])
|
102
|
+
assert_equal(asset['SerialNumber'], host.name)
|
103
|
+
assert_equal(asset['AppID'].to_s, app_id.to_s)
|
104
|
+
assert_equal(asset['StatusID'], create_status_id)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'Invalid Request: missing SerialNumber' do
|
109
|
+
# rubocop:disable Style/StringLiterals
|
110
|
+
let(:error_body) { "Name or serial number must be provided for asset records" }
|
111
|
+
let(:error) { { status: "400", msg: "", body: error_body }.to_json }
|
112
|
+
# rubocop:enable Style/StringLiterals
|
113
|
+
before do
|
114
|
+
stub_request(:post, create_path)
|
115
|
+
.with(headers: { 'Authorization' => 'Bearer ' + dummy_token,
|
116
|
+
'Content-Type' => 'application/json' },
|
117
|
+
body: create_payload)
|
118
|
+
.to_return(status: 400, body: error_body)
|
119
|
+
end
|
120
|
+
it 'raises error with status 400 for invalid payload' do
|
121
|
+
assert_raises_with_message(RuntimeError, error) do
|
122
|
+
subject.create_asset(host)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe '#request_token' do
|
129
|
+
describe 'valida credentials' do
|
130
|
+
end
|
131
|
+
it 'returns a bearer token if credentials are correct' do
|
132
|
+
assert_not_nil(subject.send(:request_token))
|
133
|
+
end
|
134
|
+
|
135
|
+
describe 'invalid credentials' do
|
136
|
+
# rubocop:disable Style/StringLiterals
|
137
|
+
let(:error_body) { "Invalid username or password." }
|
138
|
+
let(:error) { { status: "403", msg: "", body: error_body }.to_json }
|
139
|
+
# rubocop:enable Style/StringLiterals
|
140
|
+
before do
|
141
|
+
stub_request(:post, api_url + '/auth')
|
142
|
+
.with(body: auth_payload)
|
143
|
+
.to_return(status: 403, body: error_body)
|
144
|
+
end
|
145
|
+
it 'raises error with status 403' do
|
146
|
+
assert_raises_with_message(RuntimeError, error) do
|
147
|
+
subject.send(:request_token)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe '#get_asset' do
|
154
|
+
context 'Valid Request' do
|
155
|
+
before do
|
156
|
+
stub_request(:get, get_asset_path)
|
157
|
+
.with(headers: { 'Authorization' => 'Bearer ' + dummy_token })
|
158
|
+
.to_return(status: 200, body: sample_asset.to_json)
|
159
|
+
end
|
160
|
+
it 'returns asset json' do
|
161
|
+
assert_equal(subject.get_asset(sample_asset_id), sample_asset)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
def override_settings
|
2
|
+
SETTINGS[:teamdynamix] = { api: { url: 'https://api.teamdynamix.com/TDWebApi/api',
|
3
|
+
appId: '111',
|
4
|
+
username: 'a_valid_username',
|
5
|
+
password: 'a_valid_pwd' } }
|
6
|
+
end
|
7
|
+
override_settings
|
8
|
+
|
9
|
+
def get_direct_asset_attribs_val(config)
|
10
|
+
direct_attrib_fields = []
|
11
|
+
config.each do |tag, attr_name|
|
12
|
+
direct_attrib_fields << [tag, sample_asset[attr_name]]
|
13
|
+
end
|
14
|
+
direct_attrib_fields
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_nested_asset_attribs_val(config)
|
18
|
+
nested_attrib_fields = []
|
19
|
+
config.each do |tag, nested_attrib|
|
20
|
+
parent_attrib, child_attrib = nested_attrib.split(".'")
|
21
|
+
child_attrib.delete!("'")
|
22
|
+
attrib_val = sample_asset[parent_attrib].select { |attrib| attrib['Name'] == child_attrib }[0]['Value']
|
23
|
+
nested_attrib_fields << [tag, attrib_val]
|
24
|
+
end
|
25
|
+
nested_attrib_fields
|
26
|
+
end
|
27
|
+
|
28
|
+
def sample_asset_uri
|
29
|
+
api_url = SETTINGS[:teamdynamix][:api][:url]
|
30
|
+
asset_uri = api_url.split('api').first + sample_asset['Uri']
|
31
|
+
[_('URI'), link_to(sample_asset['Uri'], asset_uri, target: '_blank')]
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'webmock/minitest'
|
35
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
36
|
+
|
37
|
+
require 'fake_teamdynamix_api'
|
38
|
+
require 'test_helper'
|