snow_sync 3.1.5 → 3.1.6
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 +4 -4
- data/README.md +9 -3
- data/lib/snow_sync/Guardfile +8 -7
- data/lib/snow_sync/configs.yml +1 -0
- data/lib/snow_sync/sync_util.rb +14 -14
- data/lib/snow_sync/version.rb +1 -1
- data/spec/spec_helper.rb +2 -2
- data/spec/sync_util_mock_spec.rb +549 -0
- data/spec/sync_util_spec.rb +83 -41
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 006d22f37ee45ef3de904baba95ba0ee0e6a80dc25259741bbab58e9e34db4ee
|
4
|
+
data.tar.gz: 0e31d3394fc906562060141146dc15032c8701d7da5ec76495b9cc7d17e68bd6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f45e339f65b62bc897d697b1bb46f442cbb6a48f5ab6728701d7d1415efa91223725db708237b7637e8c34c8ad756b18d309a10a6a2a78ffeb3cc4c452637fe6
|
7
|
+
data.tar.gz: 4e3701f3d65594d6d284b99e86f8694d9565e87546570440ba89842163c1a1493aecdbbbc10e9ee755369f5e6a2ce66c351947b584b3d8bca5b8d9c13c640a20
|
data/README.md
CHANGED
@@ -62,12 +62,12 @@ cd /lib/snow_sync
|
|
62
62
|
```
|
63
63
|
|
64
64
|
* Setup the configurations in the configs.yml
|
65
|
-
*
|
65
|
+
* Supports multi-table map configurations
|
66
66
|
* YAML configuration path is the current working directory
|
67
67
|
* Append /api/now/table/ to the base_url
|
68
68
|
|
69
69
|
```bash
|
70
|
-
guard -i
|
70
|
+
bundle exec guard -i
|
71
71
|
```
|
72
72
|
|
73
73
|
**Note:** if the sync directory is deleted after a successful sync, reset the credential configs in the configs.yml so they can be re-encrypted on the next sync
|
@@ -79,7 +79,7 @@ guard -i
|
|
79
79
|
cd <path-to-the-snow-sync-dir>/gems/snow_sync-<version>
|
80
80
|
```
|
81
81
|
|
82
|
-
* Integration tests use a test record in the instance
|
82
|
+
* Integration tests use a test record in the test instance
|
83
83
|
* Setup the test configurations in the test_configs.yml
|
84
84
|
* YAML configuration path is the current working directory
|
85
85
|
* Append /api/now/table/ to the base_url
|
@@ -88,6 +88,12 @@ cd <path-to-the-snow-sync-dir>/gems/snow_sync-<version>
|
|
88
88
|
bundle exec rspec spec/sync_util_spec.rb
|
89
89
|
```
|
90
90
|
|
91
|
+
* Unit tests are pure, so they're not externally dependent
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
bundle exec rspec spec/sync_util_mock_spec.rb
|
95
|
+
```
|
96
|
+
|
91
97
|
**Note:** if the sync directory is deleted after a successful sync, reset the credential configs in the test_configs.yml so they can be re-encrypted on the next sync
|
92
98
|
|
93
99
|
## License
|
data/lib/snow_sync/Guardfile
CHANGED
@@ -16,18 +16,19 @@ yield_options = {
|
|
16
16
|
logger.info "Guard::Yield - Done!"
|
17
17
|
end,
|
18
18
|
|
19
|
-
run_on_modifications: proc do |
|
20
|
-
log.info "!!: #{files * ','}"
|
19
|
+
run_on_modifications: proc do |logger, _, files|
|
21
20
|
@util.push_modifications(files)
|
22
|
-
|
21
|
+
logger.info "!!: #{files}"
|
22
|
+
uname = `uname`.chomp.downcase
|
23
|
+
@util.notify(files, uname, logger)
|
23
24
|
end,
|
24
25
|
|
25
|
-
run_on_additions: proc do |
|
26
|
-
|
26
|
+
run_on_additions: proc do |logger, _, files|
|
27
|
+
logger.info "++: #{files}"
|
27
28
|
end,
|
28
29
|
|
29
|
-
run_on_removals: proc do |
|
30
|
-
|
30
|
+
run_on_removals: proc do |logger, _, files|
|
31
|
+
logger.warn "xx: #{files}"
|
31
32
|
end,
|
32
33
|
|
33
34
|
}
|
data/lib/snow_sync/configs.yml
CHANGED
data/lib/snow_sync/sync_util.rb
CHANGED
@@ -75,8 +75,7 @@ module SnowSync
|
|
75
75
|
# Encrypts config credentials based on previous sync
|
76
76
|
|
77
77
|
def encrypt_credentials
|
78
|
-
|
79
|
-
if !previous_sync
|
78
|
+
unless @configs["creds"]["encrypted"]
|
80
79
|
configs_path = @configs["conf_path"] + @cf
|
81
80
|
configs = YAML::load_file(configs_path)
|
82
81
|
# local configuration changes
|
@@ -84,10 +83,12 @@ module SnowSync
|
|
84
83
|
passb64 = Base64.strict_encode64(@configs["creds"]["pass"])
|
85
84
|
configs["creds"]["user"] = userb64
|
86
85
|
configs["creds"]["pass"] = passb64
|
86
|
+
configs["creds"]["encrypted"] = true
|
87
87
|
File.open(configs_path, 'w') { |f| YAML::dump(configs, f) }
|
88
88
|
# object state configuration changes
|
89
89
|
@configs["creds"]["user"] = userb64
|
90
90
|
@configs["creds"]["pass"] = passb64
|
91
|
+
@configs["creds"]["encrypted"] = true
|
91
92
|
end
|
92
93
|
end
|
93
94
|
|
@@ -155,9 +156,8 @@ module SnowSync
|
|
155
156
|
# Merges JS file changes with the encapsulated table response value
|
156
157
|
# @param [String] type Config table label
|
157
158
|
# @param [String] file JS file by name
|
158
|
-
# @param [Hash] table_hash Configured servicenow table hash
|
159
159
|
|
160
|
-
def merge_update(type, file
|
160
|
+
def merge_update(type, file)
|
161
161
|
FileUtils.cd("sync" + "/" + type)
|
162
162
|
script_body = File.open(file).read
|
163
163
|
@configs["table_map"][type]["mod"] = script_body
|
@@ -182,7 +182,7 @@ module SnowSync
|
|
182
182
|
type = path[1]
|
183
183
|
file = path[2]
|
184
184
|
table_hash = table_lookup(type, file)
|
185
|
-
merge_update(type, file
|
185
|
+
merge_update(type, file)
|
186
186
|
begin
|
187
187
|
user = Base64.strict_decode64(@configs["creds"]["user"])
|
188
188
|
pass = Base64.strict_decode64(@configs["creds"]["pass"])
|
@@ -201,22 +201,22 @@ module SnowSync
|
|
201
201
|
|
202
202
|
# Dispatches osx & linux platform notification when file updates are pushed
|
203
203
|
# @param [Array] update File updated
|
204
|
-
# @param [
|
204
|
+
# @param [String] uname Operating system name
|
205
|
+
# @param [Object] logger Logger object
|
205
206
|
|
206
|
-
def notify(update,
|
207
|
-
if
|
207
|
+
def notify(update, uname, logger)
|
208
|
+
if uname =~ /darwin/
|
208
209
|
TerminalNotifier::Guard.success(
|
209
|
-
"File: #{update
|
210
|
+
"File: #{update}",
|
210
211
|
:title => "ServiceNow Script Update"
|
211
212
|
)
|
212
|
-
|
213
|
-
elsif
|
213
|
+
logger.info "->: osx notification dispatched"
|
214
|
+
elsif uname =~ /linux/
|
214
215
|
Libnotify.show(
|
215
216
|
:summary => "ServiceNow Script Update",
|
216
|
-
:body => "File: #{update
|
217
|
-
:timeout => 1.5
|
217
|
+
:body => "File: #{update}"
|
218
218
|
)
|
219
|
-
|
219
|
+
logger.info "->: linux notification dispatched"
|
220
220
|
end
|
221
221
|
end
|
222
222
|
|
data/lib/snow_sync/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -5,8 +5,8 @@ RSpec.configure do |config|
|
|
5
5
|
require "json"
|
6
6
|
require_relative "../lib/snow_sync/sync_util.rb"
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
config.color
|
9
|
+
config.formatter = :documentation
|
10
10
|
|
11
11
|
config.expect_with :rspec do |expectations|
|
12
12
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
@@ -0,0 +1,549 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "#initialize" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
allow(YAML).to receive(:load_file)
|
7
|
+
allow(Logger).to receive(:new)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should construct an instance" do
|
11
|
+
SnowSync::SyncUtil.new(opts = "test")
|
12
|
+
expect(YAML).to have_received(:load_file).with("test_configs.yml")
|
13
|
+
expect(Logger).to have_received(:new).with(STDERR)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#create_directory" do
|
19
|
+
|
20
|
+
let :util do
|
21
|
+
SnowSync::SyncUtil.new(opts = "test")
|
22
|
+
end
|
23
|
+
|
24
|
+
let :logger do
|
25
|
+
instance_double(Logger)
|
26
|
+
end
|
27
|
+
|
28
|
+
let :dir_name do
|
29
|
+
"sync"
|
30
|
+
end
|
31
|
+
|
32
|
+
before do
|
33
|
+
allow(File).to receive(:directory?).and_return(false)
|
34
|
+
allow(FileUtils).to receive(:mkdir_p)
|
35
|
+
allow(Logger).to receive(:new).and_return(logger)
|
36
|
+
allow(logger).to receive(:info)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should create a directory" do
|
40
|
+
util.create_directory(dir_name)
|
41
|
+
expect(File).to have_received(:directory?).with(dir_name)
|
42
|
+
expect(FileUtils).to have_received(:mkdir_p).with(dir_name)
|
43
|
+
expect(logger).to have_received(:info).with("++: #{dir_name}")
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#create_subdirectory" do
|
49
|
+
|
50
|
+
let :util do
|
51
|
+
SnowSync::SyncUtil.new(opts = "test")
|
52
|
+
end
|
53
|
+
|
54
|
+
let :path do
|
55
|
+
proc { FileUtils.cd("sync") }
|
56
|
+
end
|
57
|
+
|
58
|
+
let :sub_dir_name do
|
59
|
+
"sub-dir-of-sync"
|
60
|
+
end
|
61
|
+
|
62
|
+
before do
|
63
|
+
allow_any_instance_of(SnowSync::SyncUtil).to receive(:create_directory)
|
64
|
+
.and_return("++: #{sub_dir_name}")
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should create a subdirectory" do
|
68
|
+
expect(util.create_subdirectory(sub_dir_name, &path)).to eq("++: #{sub_dir_name}")
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#create_file" do
|
74
|
+
|
75
|
+
let :util do
|
76
|
+
SnowSync::SyncUtil.new(opts = "test")
|
77
|
+
end
|
78
|
+
|
79
|
+
let :logger do
|
80
|
+
instance_double(Logger)
|
81
|
+
end
|
82
|
+
|
83
|
+
let :file_name do
|
84
|
+
"test_file"
|
85
|
+
end
|
86
|
+
|
87
|
+
let :json do
|
88
|
+
{ "test-json": "testing" }
|
89
|
+
end
|
90
|
+
|
91
|
+
let :path do
|
92
|
+
proc { }
|
93
|
+
end
|
94
|
+
|
95
|
+
before do
|
96
|
+
allow(File).to receive(:open).and_call_original
|
97
|
+
allow(Logger).to receive(:new).and_return(logger)
|
98
|
+
allow(logger).to receive(:info)
|
99
|
+
end
|
100
|
+
|
101
|
+
after do
|
102
|
+
test_file = `ls`.split("\n").pop
|
103
|
+
FileUtils.rm_rf(test_file)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should create a js file" do
|
107
|
+
util.create_file(file_name, json, &path)
|
108
|
+
expect(File).to have_received(:open).with(file_name + ".js", "w")
|
109
|
+
expect(logger).to have_received(:info).with("->: #{file_name}" + ".js")
|
110
|
+
expect(`ls`.split("\n").pop).to eq("test_file.js")
|
111
|
+
expect(File.open("#{file_name}.js").read).to eq("{:\"test-json\"=>\"testing\"}")
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "#check_required_configs" do
|
117
|
+
|
118
|
+
let :util do
|
119
|
+
SnowSync::SyncUtil.new(opts = "test")
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should raise an exception when there is no config path" do
|
123
|
+
util.configs["conf_path"] = nil
|
124
|
+
expect{util.check_required_configs}.to raise_error(
|
125
|
+
/Check the configuration path, base url, credentials or table to sync/)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should raise an exception when there is no base url" do
|
129
|
+
util.configs["base_url"] = nil
|
130
|
+
expect{util.check_required_configs}.to raise_error(
|
131
|
+
/Check the configuration path, base url, credentials or table to sync/)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should raise an exception when there is no username" do
|
135
|
+
util.configs["creds"]["user"] = nil
|
136
|
+
expect{util.check_required_configs}.to raise_error(
|
137
|
+
/Check the configuration path, base url, credentials or table to sync/)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should raise an exception when there is no password" do
|
141
|
+
util.configs["creds"]["pass"] = nil
|
142
|
+
expect{util.check_required_configs}.to raise_error(
|
143
|
+
/Check the configuration path, base url, credentials or table to sync/)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should raise an exception when there are no tables mapped" do
|
147
|
+
tables = util.configs["table_map"].keys
|
148
|
+
tables.each do |table|
|
149
|
+
util.configs["table_map"][table]["table"] = nil
|
150
|
+
expect{util.check_required_configs}.to raise_error(
|
151
|
+
/Check the configuration path, base url, credentials or table to sync/)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "#encrypt_credentials" do
|
158
|
+
|
159
|
+
let :util do
|
160
|
+
SnowSync::SyncUtil.new(opts = "test")
|
161
|
+
end
|
162
|
+
|
163
|
+
before do
|
164
|
+
FileUtils.touch("spec/test_configs.yml")
|
165
|
+
local_configs = {
|
166
|
+
"conf_path" => "spec/",
|
167
|
+
"base_url" => nil,
|
168
|
+
"creds" => {
|
169
|
+
"user" => "test-name",
|
170
|
+
"pass" => "test-password",
|
171
|
+
"encrypted" => false
|
172
|
+
}
|
173
|
+
}
|
174
|
+
allow(YAML).to receive(:load_file).and_return(local_configs)
|
175
|
+
allow(Base64).to receive(:strict_encode64).and_call_original
|
176
|
+
allow(File).to receive(:open).and_call_original
|
177
|
+
end
|
178
|
+
|
179
|
+
after do
|
180
|
+
FileUtils.rm_rf("spec/test_configs.yml")
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should encrypt credentials" do
|
184
|
+
util.configs["conf_path"] = "spec/"
|
185
|
+
util.configs["creds"]["user"] = "test-name"
|
186
|
+
util.configs["creds"]["pass"] = "test-password"
|
187
|
+
util.encrypt_credentials
|
188
|
+
expect(YAML).to have_received(:load_file).with("spec/test_configs.yml")
|
189
|
+
allow(Base64).to receive(:strict_encode64).twice
|
190
|
+
expect(File).to have_received(:open).with("spec/test_configs.yml", "w")
|
191
|
+
# configs object state updates
|
192
|
+
expect(util.configs["creds"]["user"]).to eq("dGVzdC1uYW1l")
|
193
|
+
expect(util.configs["creds"]["pass"]).to eq("dGVzdC1wYXNzd29yZA==")
|
194
|
+
expect(util.configs["creds"]["encrypted"]).to eq(true)
|
195
|
+
# test configs file updates
|
196
|
+
encrypted_content = YAML::load_file("spec/test_configs.yml")
|
197
|
+
expect(encrypted_content["creds"]["user"]).to eq("dGVzdC1uYW1l")
|
198
|
+
expect(encrypted_content["creds"]["pass"]).to eq("dGVzdC1wYXNzd29yZA==")
|
199
|
+
expect(encrypted_content["creds"]["encrypted"]).to eq(true)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "#run_setup_and_sync" do
|
204
|
+
|
205
|
+
let :util do
|
206
|
+
SnowSync::SyncUtil.new(opts = "test")
|
207
|
+
end
|
208
|
+
|
209
|
+
let :logger do
|
210
|
+
instance_double(Logger)
|
211
|
+
end
|
212
|
+
|
213
|
+
before do
|
214
|
+
allow(Base64).to receive(:strict_decode64).and_call_original
|
215
|
+
allow(Base64).to receive(:strict_encode64).and_call_original
|
216
|
+
allow(RestClient).to receive(:get).and_return("{\"result\":[{ \"status\": \"200\" }]}")
|
217
|
+
allow(FileUtils).to receive(:cd)
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should run setup & sync - sync directory present path" do
|
221
|
+
# allow not included in 'before' setup to set return bool
|
222
|
+
allow(File).to receive(:directory?).and_return(true)
|
223
|
+
expect_any_instance_of(SnowSync::SyncUtil).to receive(:create_subdirectory)
|
224
|
+
expect_any_instance_of(SnowSync::SyncUtil).to receive(:create_file)
|
225
|
+
util.configs = { "conf_path" => nil,
|
226
|
+
"base_url" => "https://test.com/api/now/table/",
|
227
|
+
"creds" => {
|
228
|
+
"user" => "dGVzdC1uYW1l",
|
229
|
+
"pass" => "dGVzdC1wYXNzd29yZA=="
|
230
|
+
},
|
231
|
+
"table_map" => {
|
232
|
+
"script_include" => {
|
233
|
+
"name" => "TestClass",
|
234
|
+
"table" => "sys_script_include",
|
235
|
+
"sys_id" => "xxxxxx-sysid-xxxxxx",
|
236
|
+
"field" => "test-field"
|
237
|
+
}
|
238
|
+
}
|
239
|
+
}
|
240
|
+
util.run_setup_and_sync
|
241
|
+
expect(File).to have_received(:directory?).with("sync")
|
242
|
+
expect(Base64).to have_received(:strict_decode64).with("dGVzdC1uYW1l")
|
243
|
+
expect(Base64).to have_received(:strict_decode64).with("dGVzdC1wYXNzd29yZA==")
|
244
|
+
expect(Base64).to have_received(:strict_encode64).with("test-name:test-password")
|
245
|
+
expect(RestClient).to have_received(:get).with(
|
246
|
+
"https://test.com/api/now/table/sys_script_include?sysparm_query=sys_id%3D" +
|
247
|
+
"xxxxxx-sysid-xxxxxx%5Ename%3DTestClass",
|
248
|
+
{:authorization => "Basic " + "dGVzdC1uYW1lOnRlc3QtcGFzc3dvcmQ=",
|
249
|
+
:accept => "application/json"})
|
250
|
+
expect(FileUtils).to have_received(:cd).with("../..")
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should run setup & sync - sync directory not present path" do
|
254
|
+
# allow not included in 'before' setup to set return bool
|
255
|
+
allow(File).to receive(:directory?).and_return(false)
|
256
|
+
expect_any_instance_of(SnowSync::SyncUtil).to receive(:create_directory)
|
257
|
+
expect_any_instance_of(SnowSync::SyncUtil).to receive(:create_subdirectory)
|
258
|
+
expect_any_instance_of(SnowSync::SyncUtil).to receive(:create_file)
|
259
|
+
util.configs = { "conf_path" => nil,
|
260
|
+
"base_url" => "https://test.com/api/now/table/",
|
261
|
+
"creds" => {
|
262
|
+
"user" => "dGVzdC1uYW1l",
|
263
|
+
"pass" => "dGVzdC1wYXNzd29yZA=="
|
264
|
+
},
|
265
|
+
"table_map" => {
|
266
|
+
"script_include" => {
|
267
|
+
"name" => "TestClass",
|
268
|
+
"table" => "sys_script_include",
|
269
|
+
"sys_id" => "xxxxxx-sysid-xxxxxx",
|
270
|
+
"field" => "test-field"
|
271
|
+
}
|
272
|
+
}
|
273
|
+
}
|
274
|
+
util.run_setup_and_sync
|
275
|
+
expect(File).to have_received(:directory?).with("sync")
|
276
|
+
expect(Base64).to have_received(:strict_decode64).with("dGVzdC1uYW1l")
|
277
|
+
expect(Base64).to have_received(:strict_decode64).with("dGVzdC1wYXNzd29yZA==")
|
278
|
+
expect(Base64).to have_received(:strict_encode64).with("test-name:test-password")
|
279
|
+
expect(RestClient).to have_received(:get).with(
|
280
|
+
"https://test.com/api/now/table/sys_script_include?sysparm_query=sys_id%3D" +
|
281
|
+
"xxxxxx-sysid-xxxxxx%5Ename%3DTestClass",
|
282
|
+
{:authorization => "Basic " + "dGVzdC1uYW1lOnRlc3QtcGFzc3dvcmQ=",
|
283
|
+
:accept => "application/json"})
|
284
|
+
expect(FileUtils).to have_received(:cd).with("../..")
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should handle an exception" do
|
288
|
+
# allow not included in 'before' setup to set return bool
|
289
|
+
allow(File).to receive(:directory?).and_return(true)
|
290
|
+
expect_any_instance_of(SnowSync::SyncUtil).to receive(:create_subdirectory)
|
291
|
+
allow(RestClient).to receive(:get).and_raise(RestClient::ExceptionWithResponse)
|
292
|
+
allow(Logger).to receive(:new).and_return(logger)
|
293
|
+
allow(logger).to receive(:error)
|
294
|
+
util.configs = { "conf_path" => nil,
|
295
|
+
"base_url" => "https://test.com/api/now/table/",
|
296
|
+
"creds" => {
|
297
|
+
"user" => "dGVzdC1uYW1l",
|
298
|
+
"pass" => "dGVzdC1wYXNzd29yZA=="
|
299
|
+
},
|
300
|
+
"table_map" => {
|
301
|
+
"script_include" => {
|
302
|
+
"name" => "TestClass",
|
303
|
+
"table" => "sys_script_include",
|
304
|
+
"sys_id" => "xxxxxx-sysid-xxxxxx",
|
305
|
+
"field" => "test-field"
|
306
|
+
}
|
307
|
+
}
|
308
|
+
}
|
309
|
+
util.run_setup_and_sync
|
310
|
+
expect(logger).to have_received(:error)
|
311
|
+
.with("ERROR: RestClient::ExceptionWithResponse").once
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
describe "#classify" do
|
317
|
+
|
318
|
+
let :util do
|
319
|
+
SnowSync::SyncUtil.new(opts = "test")
|
320
|
+
end
|
321
|
+
|
322
|
+
it "should convert snake_case string to CamelCase" do
|
323
|
+
expect(util.classify("test_class.js")).to eq("TestClass")
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
describe "#table_lookup" do
|
329
|
+
|
330
|
+
let :util do
|
331
|
+
SnowSync::SyncUtil.new(opts = "test")
|
332
|
+
end
|
333
|
+
|
334
|
+
it "should return configured SN table" do
|
335
|
+
util.configs = { "conf_path" => nil,
|
336
|
+
"base_url" => "https://test.com/api/now/table/",
|
337
|
+
"creds" => {
|
338
|
+
"user" => "dGVzdC1uYW1l",
|
339
|
+
"pass" => "dGVzdC1wYXNzd29yZA=="
|
340
|
+
},
|
341
|
+
"table_map" => {
|
342
|
+
"script_include" => {
|
343
|
+
"name" => "TestClass",
|
344
|
+
"table" => "sys_script_include",
|
345
|
+
"sys_id" => "xxxxxx-sysid-xxxxxx",
|
346
|
+
"field" => "test-field"
|
347
|
+
}
|
348
|
+
}
|
349
|
+
}
|
350
|
+
table_map = util.table_lookup("script_include", "test_class.js")
|
351
|
+
expect(table_map == { "name" => "TestClass",
|
352
|
+
"table" => "sys_script_include",
|
353
|
+
"sys_id" => "xxxxxx-sysid-xxxxxx",
|
354
|
+
"field" => "test-field"
|
355
|
+
}).to be(true)
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
describe "#merge_update" do
|
361
|
+
|
362
|
+
let! :util do
|
363
|
+
SnowSync::SyncUtil.new(opts = "test")
|
364
|
+
end
|
365
|
+
|
366
|
+
before do
|
367
|
+
allow(FileUtils).to receive(:cd)
|
368
|
+
allow(File).to receive(:open).and_return(IO)
|
369
|
+
allow(IO).to receive(:read).and_return("var x = 10;")
|
370
|
+
end
|
371
|
+
|
372
|
+
it "should merge a script change" do
|
373
|
+
util.configs = { "table_map" => {
|
374
|
+
"script_include" => {
|
375
|
+
"name" => "TestClass",
|
376
|
+
"table" => "sys_script_include",
|
377
|
+
"sys_id" => "xxxxxx-sysid-xxxxxx",
|
378
|
+
"field" => "test-field"
|
379
|
+
}
|
380
|
+
}
|
381
|
+
}
|
382
|
+
util.merge_update("script_include", "test_class.js")
|
383
|
+
expect(File).to have_received(:open).with("test_class.js")
|
384
|
+
expect(IO).to have_received(:read)
|
385
|
+
expect(FileUtils).to have_received(:cd).twice
|
386
|
+
expect(util.configs == { "table_map" => {
|
387
|
+
"script_include" => {
|
388
|
+
"name" => "TestClass",
|
389
|
+
"table" => "sys_script_include",
|
390
|
+
"sys_id" => "xxxxxx-sysid-xxxxxx",
|
391
|
+
"field" => "test-field",
|
392
|
+
"mod" => "var x = 10;"
|
393
|
+
}
|
394
|
+
}}).to be(true)
|
395
|
+
end
|
396
|
+
|
397
|
+
end
|
398
|
+
|
399
|
+
describe "#start_sync" do
|
400
|
+
|
401
|
+
let :util do
|
402
|
+
SnowSync::SyncUtil.new(opts = "test")
|
403
|
+
end
|
404
|
+
|
405
|
+
before do
|
406
|
+
allow_any_instance_of(SnowSync::SyncUtil).to receive(:check_required_configs)
|
407
|
+
allow_any_instance_of(SnowSync::SyncUtil).to receive(:encrypt_credentials)
|
408
|
+
allow_any_instance_of(SnowSync::SyncUtil).to receive(:run_setup_and_sync)
|
409
|
+
.and_return("sync-and-setup-complete!")
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should create a subdirectory" do
|
413
|
+
expect(util.start_sync).to eq("sync-and-setup-complete!")
|
414
|
+
end
|
415
|
+
|
416
|
+
end
|
417
|
+
|
418
|
+
describe "#push_modifications" do
|
419
|
+
|
420
|
+
let :util do
|
421
|
+
SnowSync::SyncUtil.new(opts = "test")
|
422
|
+
end
|
423
|
+
|
424
|
+
let :logger do
|
425
|
+
instance_double(Logger)
|
426
|
+
end
|
427
|
+
|
428
|
+
before do
|
429
|
+
allow_any_instance_of(SnowSync::SyncUtil).to receive(:table_lookup)
|
430
|
+
.and_return({
|
431
|
+
"name" => "TestClass",
|
432
|
+
"table" => "sys_script_include",
|
433
|
+
"sys_id" => "xxxxxx-sysid-xxxxxx",
|
434
|
+
"field" => "test-field",
|
435
|
+
"mod" => "var x = 10;"
|
436
|
+
})
|
437
|
+
allow_any_instance_of(SnowSync::SyncUtil).to receive(:merge_update)
|
438
|
+
allow(Base64).to receive(:strict_decode64).and_call_original
|
439
|
+
allow(Base64).to receive(:strict_encode64).and_call_original
|
440
|
+
allow(RestClient).to receive(:patch).and_return("{\"result\":[{ \"status\": \"201\" }]}")
|
441
|
+
end
|
442
|
+
|
443
|
+
it "should push modifications to the instance" do
|
444
|
+
util.configs = { "conf_path" => nil,
|
445
|
+
"base_url" => "https://test.com/api/now/table/",
|
446
|
+
"creds" => {
|
447
|
+
"user" => "dGVzdC1uYW1l",
|
448
|
+
"pass" => "dGVzdC1wYXNzd29yZA=="
|
449
|
+
},
|
450
|
+
"table_map" => {
|
451
|
+
"script_include" => {
|
452
|
+
"name" => "TestClass",
|
453
|
+
"table" => "sys_script_include",
|
454
|
+
"sys_id" => "xxxxxx-sysid-xxxxxx",
|
455
|
+
"field" => "test-field",
|
456
|
+
"mod" => "var x = 10;"
|
457
|
+
}
|
458
|
+
}
|
459
|
+
}
|
460
|
+
util.push_modifications(["sync/script_include/test_class.js"])
|
461
|
+
expect(Base64).to have_received(:strict_decode64).with("dGVzdC1uYW1l")
|
462
|
+
expect(Base64).to have_received(:strict_decode64).with("dGVzdC1wYXNzd29yZA==")
|
463
|
+
expect(Base64).to have_received(:strict_encode64).with("test-name:test-password")
|
464
|
+
expect(RestClient).to have_received(:patch).with(
|
465
|
+
"https://test.com/api/now/table/sys_script_include/xxxxxx-sysid-xxxxxx",
|
466
|
+
"{\"test-field\":\"var x = 10;\"}",
|
467
|
+
{:authorization => "Basic " + "dGVzdC1uYW1lOnRlc3QtcGFzc3dvcmQ=",
|
468
|
+
:content_type => "application/json", :accept => "application/json"})
|
469
|
+
end
|
470
|
+
|
471
|
+
it "should handle an exception" do
|
472
|
+
allow(RestClient).to receive(:patch).and_raise(RestClient::ExceptionWithResponse)
|
473
|
+
allow(Logger).to receive(:new).and_return(logger)
|
474
|
+
allow(logger).to receive(:error)
|
475
|
+
util.configs = { "conf_path" => nil,
|
476
|
+
"base_url" => "https://test.com/api/now/table/",
|
477
|
+
"creds" => {
|
478
|
+
"user" => "dGVzdC1uYW1l",
|
479
|
+
"pass" => "dGVzdC1wYXNzd29yZA=="
|
480
|
+
},
|
481
|
+
"table_map" => {
|
482
|
+
"script_include" => {
|
483
|
+
"name" => "TestClass",
|
484
|
+
"table" => "sys_script_include",
|
485
|
+
"sys_id" => "xxxxxx-sysid-xxxxxx",
|
486
|
+
"field" => "test-field",
|
487
|
+
"mod" => "var x = 10;"
|
488
|
+
}
|
489
|
+
}
|
490
|
+
}
|
491
|
+
util.push_modifications(["sync/script_include/test_class.js"])
|
492
|
+
expect(logger).to have_received(:error)
|
493
|
+
.with("ERROR: RestClient::ExceptionWithResponse").once
|
494
|
+
end
|
495
|
+
|
496
|
+
end
|
497
|
+
|
498
|
+
describe "#notify" do
|
499
|
+
|
500
|
+
let :util do
|
501
|
+
SnowSync::SyncUtil.new(opts = "test")
|
502
|
+
end
|
503
|
+
|
504
|
+
let :logger do
|
505
|
+
instance_double(Logger)
|
506
|
+
end
|
507
|
+
|
508
|
+
let :update do
|
509
|
+
"test_class"
|
510
|
+
end
|
511
|
+
|
512
|
+
let :macosx? do
|
513
|
+
`uname`.chomp.downcase == "darwin"
|
514
|
+
end
|
515
|
+
|
516
|
+
let :linux? do
|
517
|
+
`uname`.chomp.downcase == "linux"
|
518
|
+
end
|
519
|
+
|
520
|
+
before do
|
521
|
+
allow(TerminalNotifier::Guard).to receive(:success) if macosx?
|
522
|
+
allow(Libnotify).to receive(:show) if linux?
|
523
|
+
allow(Logger).to receive(:new).and_return(logger)
|
524
|
+
allow(logger).to receive(:info)
|
525
|
+
end
|
526
|
+
|
527
|
+
condition = `uname`.chomp.downcase == "darwin"
|
528
|
+
context "when true", if: condition do
|
529
|
+
it "should send notification - macosx path" do
|
530
|
+
uname = "darwin"
|
531
|
+
util.notify(update, uname, util.logger)
|
532
|
+
expect(TerminalNotifier::Guard).to have_received(:success)
|
533
|
+
.with("File: #{update}", :title => "ServiceNow Script Update")
|
534
|
+
expect(logger).to have_received(:info).with("->: osx notification dispatched")
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
condition = `uname`.chomp.downcase == "linux"
|
539
|
+
context "when true", if: condition do
|
540
|
+
it "should send notification - linux path" do
|
541
|
+
uname = "linux"
|
542
|
+
util.notify(update, uname, util.logger)
|
543
|
+
expect(Libnotify).to have_received(:show)
|
544
|
+
.with(:summary => "ServiceNow Script Update", :body => "File: #{update}")
|
545
|
+
expect(logger).to have_received(:info).with("->: linux notification dispatched")
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
end
|
data/spec/sync_util_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe "utility object" do
|
4
4
|
|
5
|
-
let
|
5
|
+
let :util do
|
6
6
|
SnowSync::SyncUtil.new(opts = "test")
|
7
7
|
end
|
8
8
|
|
@@ -20,22 +20,22 @@ describe "utility object" do
|
|
20
20
|
|
21
21
|
end
|
22
22
|
|
23
|
-
describe "create_directory" do
|
23
|
+
describe "#create_directory" do
|
24
24
|
|
25
|
-
let
|
25
|
+
let :util do
|
26
26
|
SnowSync::SyncUtil.new(opts = "test")
|
27
27
|
end
|
28
28
|
|
29
|
-
let! :created_time do
|
30
|
-
util.create_directory("sync")
|
31
|
-
return File.ctime("sync")
|
32
|
-
end
|
33
|
-
|
34
29
|
it "should create a directory" do
|
30
|
+
util.create_directory("sync")
|
35
31
|
dir = `ls`.split("\n")
|
36
32
|
expect(dir.include?("sync")).to eq true
|
37
33
|
end
|
38
34
|
|
35
|
+
let :created_time do
|
36
|
+
File.ctime("sync")
|
37
|
+
end
|
38
|
+
|
39
39
|
it "should not create directory" do
|
40
40
|
util.create_directory("sync")
|
41
41
|
check_created_time = File.ctime("sync")
|
@@ -44,15 +44,22 @@ describe "create_directory" do
|
|
44
44
|
|
45
45
|
end
|
46
46
|
|
47
|
-
describe "create_file" do
|
47
|
+
describe "#create_file" do
|
48
48
|
|
49
|
-
let
|
49
|
+
let :util do
|
50
50
|
SnowSync::SyncUtil.new(opts = "test")
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
|
-
FileUtils.mkdir_p("sync")
|
53
|
+
before do
|
55
54
|
FileUtils.mkdir_p("sync/test_sub_dir")
|
55
|
+
end
|
56
|
+
|
57
|
+
after do
|
58
|
+
FileUtils.cd("../..")
|
59
|
+
FileUtils.rm_rf("sync")
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should create a file" do
|
56
63
|
json = { "property" => "value" }
|
57
64
|
name = "TestClass".snakecase
|
58
65
|
path = proc do
|
@@ -60,13 +67,11 @@ describe "create_file" do
|
|
60
67
|
end
|
61
68
|
util.create_file(name, json, &path)
|
62
69
|
expect(File.exists?("test_class.js")).to eq true
|
63
|
-
FileUtils.cd("../..")
|
64
|
-
FileUtils.rm_rf("sync")
|
65
70
|
end
|
66
71
|
|
67
72
|
end
|
68
73
|
|
69
|
-
describe "check_required_configs" do
|
74
|
+
describe "#check_required_configs" do
|
70
75
|
|
71
76
|
let :util do
|
72
77
|
SnowSync::SyncUtil.new(opts = "test")
|
@@ -74,35 +79,40 @@ describe "check_required_configs" do
|
|
74
79
|
|
75
80
|
it "should raise an exception when there is no config path" do
|
76
81
|
util.configs["conf_path"] = nil
|
77
|
-
expect{util.check_required_configs}.to raise_error(
|
82
|
+
expect{util.check_required_configs}.to raise_error(
|
83
|
+
/Check the configuration path, base url, credentials or table to sync/)
|
78
84
|
end
|
79
85
|
|
80
86
|
it "should raise an exception when there is no base url" do
|
81
87
|
util.configs["base_url"] = nil
|
82
|
-
expect{util.check_required_configs}.to raise_error(
|
88
|
+
expect{util.check_required_configs}.to raise_error(
|
89
|
+
/Check the configuration path, base url, credentials or table to sync/)
|
83
90
|
end
|
84
91
|
|
85
92
|
it "should raise an exception when there is no username" do
|
86
93
|
util.configs["creds"]["user"] = nil
|
87
|
-
expect{util.check_required_configs}.to raise_error(
|
94
|
+
expect{util.check_required_configs}.to raise_error(
|
95
|
+
/Check the configuration path, base url, credentials or table to sync/)
|
88
96
|
end
|
89
97
|
|
90
98
|
it "should raise an exception when there is no password" do
|
91
99
|
util.configs["creds"]["pass"] = nil
|
92
|
-
expect{util.check_required_configs}.to raise_error(
|
100
|
+
expect{util.check_required_configs}.to raise_error(
|
101
|
+
/Check the configuration path, base url, credentials or table to sync/)
|
93
102
|
end
|
94
103
|
|
95
104
|
it "should raise an exception when there are no tables mapped" do
|
96
105
|
tables = util.configs["table_map"].keys
|
97
106
|
tables.each do |table|
|
98
107
|
util.configs["table_map"][table]["table"] = nil
|
99
|
-
expect{util.check_required_configs}.to raise_error(
|
108
|
+
expect{util.check_required_configs}.to raise_error(
|
109
|
+
/Check the configuration path, base url, credentials or table to sync/)
|
100
110
|
end
|
101
111
|
end
|
102
112
|
|
103
113
|
end
|
104
114
|
|
105
|
-
describe "classify" do
|
115
|
+
describe "#classify" do
|
106
116
|
|
107
117
|
let :util do
|
108
118
|
SnowSync::SyncUtil.new(opts = "test")
|
@@ -117,7 +127,7 @@ describe "classify" do
|
|
117
127
|
|
118
128
|
end
|
119
129
|
|
120
|
-
describe "table_lookup" do
|
130
|
+
describe "#table_lookup" do
|
121
131
|
|
122
132
|
let :util do
|
123
133
|
SnowSync::SyncUtil.new(opts = "test")
|
@@ -131,15 +141,21 @@ describe "table_lookup" do
|
|
131
141
|
|
132
142
|
end
|
133
143
|
|
134
|
-
describe "merge_update" do
|
144
|
+
describe "#merge_update" do
|
135
145
|
|
136
|
-
let
|
146
|
+
let :util do
|
137
147
|
SnowSync::SyncUtil.new(opts = "test")
|
138
148
|
end
|
139
149
|
|
150
|
+
before do
|
151
|
+
FileUtils.mkdir_p("sync/script_include")
|
152
|
+
end
|
153
|
+
|
154
|
+
after do
|
155
|
+
FileUtils.rm_rf("sync")
|
156
|
+
end
|
157
|
+
|
140
158
|
it "should merge script with the configs object" do
|
141
|
-
FileUtils.mkdir_p("sync")
|
142
|
-
FileUtils.mkdir_p("sync/script_include")
|
143
159
|
json_resp = "var test = 'test'; \n" +
|
144
160
|
"var testing = function(arg) { \n\tgs.print(arg) \n}; \n" +
|
145
161
|
"testing('test');"
|
@@ -153,25 +169,30 @@ describe "merge_update" do
|
|
153
169
|
path = file.split("/")
|
154
170
|
type = path[1]
|
155
171
|
file = path[2]
|
156
|
-
|
157
|
-
util.merge_update(type, file, table_map)
|
172
|
+
util.merge_update(type, file)
|
158
173
|
expect(util.configs["table_map"]["script_include"]["mod"] != nil).to eq true
|
159
|
-
FileUtils.rm_rf("sync")
|
160
174
|
end
|
161
175
|
|
162
176
|
end
|
163
177
|
|
164
|
-
describe "
|
178
|
+
describe "#run_setup_and_sync" do
|
165
179
|
|
166
|
-
let
|
180
|
+
let :util do
|
167
181
|
SnowSync::SyncUtil.new(opts = "test")
|
168
182
|
end
|
169
183
|
|
170
|
-
|
184
|
+
before do
|
185
|
+
util.encrypt_credentials
|
186
|
+
end
|
187
|
+
|
188
|
+
after do
|
189
|
+
FileUtils.rm_rf("sync")
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should setup file locally and sync code from the instance" do
|
171
193
|
util.run_setup_and_sync
|
172
194
|
file = File.open("sync/script_include/test_class.js")
|
173
195
|
expect(file.is_a?(Object)).to eq true
|
174
|
-
FileUtils.rm_rf("sync")
|
175
196
|
end
|
176
197
|
|
177
198
|
end
|
@@ -182,23 +203,32 @@ describe "push_modifications - single table configuration" do
|
|
182
203
|
SnowSync::SyncUtil.new(opts = "test")
|
183
204
|
end
|
184
205
|
|
206
|
+
before do
|
207
|
+
util.encrypt_credentials
|
208
|
+
end
|
209
|
+
|
210
|
+
after do
|
211
|
+
FileUtils.rm_rf("sync")
|
212
|
+
end
|
213
|
+
|
185
214
|
it "should push modifications to a configured instance" do
|
186
215
|
util.run_setup_and_sync
|
187
216
|
file = File.open("sync/script_include/test_class.js", "r+")
|
188
217
|
lines = file.readlines
|
189
218
|
file.close
|
190
|
-
lines[0] = "// test comment
|
219
|
+
lines[0] = "// test comment - single push \n"
|
191
220
|
newfile = File.new("sync/script_include/test_class.js", "w")
|
192
221
|
lines.each do |line|
|
193
222
|
newfile.write(line)
|
194
223
|
end
|
195
224
|
newfile.close
|
196
225
|
util.push_modifications(["sync/script_include/test_class.js"])
|
226
|
+
# resync confirms mods were pushed to the instance
|
197
227
|
util.run_setup_and_sync
|
198
228
|
file = File.open("sync/script_include/test_class.js", "r+")
|
199
229
|
lines = file.readlines
|
200
230
|
file.close
|
201
|
-
expect(lines[0]).to eq "// test comment
|
231
|
+
expect(lines[0]).to eq "// test comment - single push \n"
|
202
232
|
end
|
203
233
|
|
204
234
|
end
|
@@ -209,7 +239,15 @@ describe "push_modifications - mutli-table configuration" do
|
|
209
239
|
SnowSync::SyncUtil.new(opts = "test")
|
210
240
|
end
|
211
241
|
|
212
|
-
|
242
|
+
before do
|
243
|
+
util.encrypt_credentials
|
244
|
+
end
|
245
|
+
|
246
|
+
after do
|
247
|
+
FileUtils.rm_rf("sync")
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should push modifications to a configured instance" do
|
213
251
|
def do_edit(file, edit)
|
214
252
|
file = File.open(file, "r+")
|
215
253
|
lines = file.readlines
|
@@ -229,14 +267,18 @@ describe "push_modifications - mutli-table configuration" do
|
|
229
267
|
end
|
230
268
|
util.run_setup_and_sync
|
231
269
|
# sys_script_include
|
232
|
-
do_edit(
|
270
|
+
do_edit(
|
271
|
+
"sync/script_include/test_class.js", "// test comment - multi push 1\n")
|
233
272
|
# sys_ui_action
|
234
|
-
do_edit(
|
273
|
+
do_edit(
|
274
|
+
"sync/ui_action/test_action.js", "// test comment - multi push 2\n")
|
235
275
|
# queued mods, push in sequence
|
236
|
-
util.push_modifications(
|
276
|
+
util.push_modifications(
|
277
|
+
["sync/script_include/test_class.js", "sync/ui_action/test_action.js"])
|
278
|
+
# resync confirms mods were pushed to the instance
|
237
279
|
util.run_setup_and_sync
|
238
|
-
run_check("sync/script_include/test_class.js", "// test comment
|
239
|
-
run_check("sync/ui_action/test_action.js", "// test comment
|
280
|
+
run_check("sync/script_include/test_class.js", "// test comment - multi push 1\n")
|
281
|
+
run_check("sync/ui_action/test_action.js", "// test comment - multi push 2\n")
|
240
282
|
end
|
241
283
|
|
242
284
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: snow_sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Wallace
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-01-
|
11
|
+
date: 2022-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -197,6 +197,7 @@ files:
|
|
197
197
|
- lib/snow_sync/version.rb
|
198
198
|
- spec/docker_install_spec.rb
|
199
199
|
- spec/spec_helper.rb
|
200
|
+
- spec/sync_util_mock_spec.rb
|
200
201
|
- spec/sync_util_spec.rb
|
201
202
|
homepage: https://rubygems.org/gems/snow_sync
|
202
203
|
licenses:
|