foreman_teamdynamix 0.0.1
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/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'
|