embulk-input-zendesk 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.ruby-version +1 -0
- data/.travis.yml +44 -0
- data/.travis.yml.erb +43 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +21 -0
- data/README.md +51 -0
- data/Rakefile +21 -0
- data/embulk-input-zendesk.gemspec +28 -0
- data/gemfiles/embulk-0.8.0-latest +4 -0
- data/gemfiles/embulk-0.8.1 +4 -0
- data/gemfiles/embulk-latest +4 -0
- data/gemfiles/template.erb +4 -0
- data/lib/embulk/input/zendesk.rb +9 -0
- data/lib/embulk/input/zendesk/client.rb +199 -0
- data/lib/embulk/input/zendesk/plugin.rb +138 -0
- data/test/capture_io.rb +45 -0
- data/test/embulk/input/zendesk/test_client.rb +469 -0
- data/test/embulk/input/zendesk/test_plugin.rb +338 -0
- data/test/fixture_helper.rb +11 -0
- data/test/fixtures/invalid_lack_username.yml +9 -0
- data/test/fixtures/invalid_unknown_auth.yml +9 -0
- data/test/fixtures/tickets.json +44 -0
- data/test/fixtures/valid_auth_basic.yml +11 -0
- data/test/fixtures/valid_auth_oauth.yml +10 -0
- data/test/fixtures/valid_auth_token.yml +11 -0
- data/test/override_assert_raise.rb +21 -0
- data/test/run-test.rb +26 -0
- metadata +253 -0
@@ -0,0 +1,338 @@
|
|
1
|
+
require "embulk"
|
2
|
+
Embulk.setup
|
3
|
+
|
4
|
+
require "yaml"
|
5
|
+
require "embulk/input/zendesk"
|
6
|
+
require "override_assert_raise"
|
7
|
+
require "fixture_helper"
|
8
|
+
require "capture_io"
|
9
|
+
|
10
|
+
module Embulk
|
11
|
+
module Input
|
12
|
+
module Zendesk
|
13
|
+
class TestPlugin < Test::Unit::TestCase
|
14
|
+
include OverrideAssertRaise
|
15
|
+
include FixtureHelper
|
16
|
+
include CaptureIo
|
17
|
+
|
18
|
+
def run_with(yml)
|
19
|
+
silence do
|
20
|
+
Embulk::Runner.run(YAML.load fixture_load(yml))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
sub_test_case "exec" do
|
25
|
+
setup do
|
26
|
+
stub(Plugin).resume { Hash.new }
|
27
|
+
end
|
28
|
+
|
29
|
+
test "run with valid.yml (basic)" do
|
30
|
+
assert_nothing_raised do
|
31
|
+
run_with("valid_auth_basic.yml")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
test "run with valid.yml (token)" do
|
36
|
+
assert_nothing_raised do
|
37
|
+
run_with("valid_auth_token.yml")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
test "run with valid.yml (oauth)" do
|
42
|
+
assert_nothing_raised do
|
43
|
+
run_with("valid_auth_oauth.yml")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
test "run with invalid username lack" do
|
48
|
+
# NOTE: will be raised Java::OrgEmbulkExec::PartialExecutionException, not ConfigError. It is Embulk internally exception handling matter.
|
49
|
+
assert_raise do
|
50
|
+
run_with("invalid_lack_username.yml")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
sub_test_case ".transaction" do
|
56
|
+
setup do
|
57
|
+
stub(Plugin).resume { Hash.new }
|
58
|
+
@control = proc { Hash.new }
|
59
|
+
end
|
60
|
+
|
61
|
+
def config(yml)
|
62
|
+
conf = YAML.load fixture_load(yml)
|
63
|
+
Embulk::DataSource.new(conf["in"])
|
64
|
+
end
|
65
|
+
|
66
|
+
test "lack username config" do
|
67
|
+
assert_raise(ConfigError) do
|
68
|
+
Plugin.transaction(config("invalid_lack_username.yml"), &@control)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
test "unknown auth_method" do
|
73
|
+
assert_raise(ConfigError) do
|
74
|
+
Plugin.transaction(config("invalid_unknown_auth.yml"), &@control)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
test "invoke Client#validate_config" do
|
79
|
+
any_instance_of(Client) do |klass|
|
80
|
+
mock(klass).validate_config
|
81
|
+
end
|
82
|
+
Plugin.transaction(config("valid_auth_oauth.yml"), &@control)
|
83
|
+
end
|
84
|
+
|
85
|
+
test "run as well" do
|
86
|
+
actual = nil
|
87
|
+
assert_nothing_raised do
|
88
|
+
actual = Plugin.transaction(config("valid_auth_oauth.yml"), &@control)
|
89
|
+
end
|
90
|
+
|
91
|
+
expected = {}
|
92
|
+
assert_equal expected, actual
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
sub_test_case ".guess" do
|
97
|
+
setup do
|
98
|
+
@client = Client.new(task)
|
99
|
+
stub(Client).new { @client }
|
100
|
+
@httpclient = @client.httpclient
|
101
|
+
stub(@client).httpclient { @httpclient }
|
102
|
+
end
|
103
|
+
|
104
|
+
test "invoke Client#validate_config" do
|
105
|
+
@httpclient.test_loopback_http_response << [
|
106
|
+
"HTTP/1.1 200",
|
107
|
+
"Content-Type: application/json",
|
108
|
+
"",
|
109
|
+
{
|
110
|
+
tickets: [
|
111
|
+
JSON.parse(fixture_load("tickets.json"))
|
112
|
+
]
|
113
|
+
}.to_json
|
114
|
+
].join("\r\n")
|
115
|
+
mock(@client).validate_config
|
116
|
+
Plugin.guess(config)["columns"]
|
117
|
+
end
|
118
|
+
|
119
|
+
test "guessing" do
|
120
|
+
@httpclient.test_loopback_http_response << [
|
121
|
+
"HTTP/1.1 200",
|
122
|
+
"Content-Type: application/json",
|
123
|
+
"",
|
124
|
+
{
|
125
|
+
tickets: [
|
126
|
+
JSON.parse(fixture_load("tickets.json"))
|
127
|
+
]
|
128
|
+
}.to_json
|
129
|
+
].join("\r\n")
|
130
|
+
actual = Plugin.guess(config)["columns"]
|
131
|
+
assert actual.include?(name: "url", type: :string)
|
132
|
+
assert actual.include?(name: "id", type: :long)
|
133
|
+
assert actual.include?(name: "created_at", type: :timestamp, format: "%Y-%m-%dT%H:%M:%S%z")
|
134
|
+
assert actual.include?(name: "has_incidents", type: :boolean)
|
135
|
+
|
136
|
+
# TODO: re-enable these json type tests after this plugin officially support it
|
137
|
+
# assert actual.include?(name: "tags", type: :json)
|
138
|
+
# assert actual.include?(name: "collaborator_ids", type: :json)
|
139
|
+
|
140
|
+
# assert actual.include?(name: "custom_fields", type: :json)
|
141
|
+
# assert actual.include?(name: "satisfaction_rating", type: :json)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
sub_test_case "#run" do
|
146
|
+
def page_builder
|
147
|
+
@page_builder ||= Object.new
|
148
|
+
end
|
149
|
+
|
150
|
+
def schema
|
151
|
+
[
|
152
|
+
{"name" => "id", "type" => "long"}
|
153
|
+
]
|
154
|
+
end
|
155
|
+
|
156
|
+
def run_task
|
157
|
+
task.merge({
|
158
|
+
schema: schema,
|
159
|
+
retry_limit: 1,
|
160
|
+
retry_initial_wait_sec: 0,
|
161
|
+
})
|
162
|
+
end
|
163
|
+
|
164
|
+
setup do
|
165
|
+
@client = Client.new(run_task)
|
166
|
+
stub(Client).new { @client }
|
167
|
+
@httpclient = @client.httpclient
|
168
|
+
stub(@client).httpclient { @httpclient }
|
169
|
+
@plugin = Plugin.new(run_task, nil, nil, page_builder)
|
170
|
+
end
|
171
|
+
|
172
|
+
sub_test_case "preview" do
|
173
|
+
setup do
|
174
|
+
stub(@plugin).preview? { true }
|
175
|
+
end
|
176
|
+
|
177
|
+
test "call tickets method instead of ticket_all" do
|
178
|
+
mock(@client).export(anything, "tickets", anything) { [] }
|
179
|
+
mock(@client).incremental_export.never
|
180
|
+
mock(page_builder).finish
|
181
|
+
|
182
|
+
@plugin.run
|
183
|
+
end
|
184
|
+
|
185
|
+
test "task[:schema] columns passed into page_builder.add" do
|
186
|
+
tickets = [
|
187
|
+
{"id" => 1, "created_at" => "2000-01-01T00:00:00+0900"},
|
188
|
+
{"id" => 2, "created_at" => "2000-01-01T00:00:00+0900"},
|
189
|
+
]
|
190
|
+
|
191
|
+
@httpclient.test_loopback_http_response << [
|
192
|
+
"HTTP/1.1 200",
|
193
|
+
"Content-Type: application/json",
|
194
|
+
"",
|
195
|
+
{
|
196
|
+
tickets: tickets
|
197
|
+
}.to_json
|
198
|
+
].join("\r\n")
|
199
|
+
|
200
|
+
tickets.each do |ticket|
|
201
|
+
mock(page_builder).add([ticket["id"]])
|
202
|
+
end
|
203
|
+
mock(page_builder).finish
|
204
|
+
|
205
|
+
@plugin.run
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
sub_test_case "run" do
|
210
|
+
setup do
|
211
|
+
stub(@plugin).preview? { false }
|
212
|
+
end
|
213
|
+
|
214
|
+
test "call ticket_all method instead of tickets" do
|
215
|
+
mock(@client).export.never
|
216
|
+
mock(@client).incremental_export(anything, "tickets", 0, []) { [] }
|
217
|
+
mock(page_builder).finish
|
218
|
+
|
219
|
+
@plugin.run
|
220
|
+
end
|
221
|
+
|
222
|
+
test "task[:schema] columns passed into page_builder.add" do
|
223
|
+
tickets = [
|
224
|
+
{"id" => 1, "created_at" => "2000-01-01T00:00:00+0900"},
|
225
|
+
{"id" => 2, "created_at" => "2000-01-01T00:00:00+0900"},
|
226
|
+
]
|
227
|
+
|
228
|
+
@httpclient.test_loopback_http_response << [
|
229
|
+
"HTTP/1.1 200",
|
230
|
+
"Content-Type: application/json",
|
231
|
+
"",
|
232
|
+
{
|
233
|
+
tickets: tickets
|
234
|
+
}.to_json
|
235
|
+
].join("\r\n")
|
236
|
+
|
237
|
+
tickets.each do |ticket|
|
238
|
+
mock(page_builder).add([ticket["id"]])
|
239
|
+
end
|
240
|
+
mock(page_builder).finish
|
241
|
+
|
242
|
+
@plugin.run
|
243
|
+
end
|
244
|
+
|
245
|
+
sub_test_case "casting value" do
|
246
|
+
setup do
|
247
|
+
stub(@plugin).preview? { false }
|
248
|
+
@httpclient.test_loopback_http_response << [
|
249
|
+
"HTTP/1.1 200",
|
250
|
+
"Content-Type: application/json",
|
251
|
+
"",
|
252
|
+
{
|
253
|
+
tickets: data
|
254
|
+
}.to_json
|
255
|
+
].join("\r\n")
|
256
|
+
end
|
257
|
+
|
258
|
+
def schema
|
259
|
+
[
|
260
|
+
{"name" => "target_l", "type" => "long"},
|
261
|
+
{"name" => "target_f", "type" => "double"},
|
262
|
+
{"name" => "target_str", "type" => "string"},
|
263
|
+
{"name" => "target_bool", "type" => "boolean"},
|
264
|
+
{"name" => "target_time", "type" => "timestamp"},
|
265
|
+
]
|
266
|
+
end
|
267
|
+
|
268
|
+
def data
|
269
|
+
[
|
270
|
+
{
|
271
|
+
"id" => 1, "target_l" => "3", "target_f" => "3", "target_str" => "str",
|
272
|
+
"target_bool" => false, "target_time" => "2000-01-01",
|
273
|
+
},
|
274
|
+
{
|
275
|
+
"id" => 2, "target_l" => 4.5, "target_f" => 4.5, "target_str" => 999,
|
276
|
+
"target_bool" => "truthy", "target_time" => Time.parse("1999-01-01"),
|
277
|
+
},
|
278
|
+
]
|
279
|
+
end
|
280
|
+
|
281
|
+
test "cast as given type" do
|
282
|
+
mock(page_builder).add([3, 3.0, "str", false, Time.parse("2000-01-01")])
|
283
|
+
mock(page_builder).add([4, 4.5, "999", true, Time.parse("1999-01-01")])
|
284
|
+
mock(page_builder).finish
|
285
|
+
|
286
|
+
@plugin.run
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
sub_test_case "start_time option not given" do
|
291
|
+
test "Nothing passed to client" do
|
292
|
+
stub(page_builder).finish
|
293
|
+
|
294
|
+
mock(@client).tickets(false)
|
295
|
+
@plugin.run
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
sub_test_case "start_time option given" do
|
300
|
+
def run_task
|
301
|
+
task.merge({
|
302
|
+
start_time: "2000-01-01T00:00:00+0000",
|
303
|
+
schema: schema,
|
304
|
+
retry_limit: 1,
|
305
|
+
retry_initial_wait_sec: 0,
|
306
|
+
})
|
307
|
+
end
|
308
|
+
|
309
|
+
test "Passed to client as integer (epoch)" do
|
310
|
+
stub(page_builder).finish
|
311
|
+
|
312
|
+
start_time = Time.parse(run_task[:start_time]).to_i
|
313
|
+
mock(@client).tickets(false, start_time)
|
314
|
+
@plugin.run
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
|
321
|
+
def yml
|
322
|
+
"valid_auth_basic.yml"
|
323
|
+
end
|
324
|
+
|
325
|
+
def config
|
326
|
+
conf = YAML.load fixture_load(yml)
|
327
|
+
Embulk::DataSource.new(conf["in"])
|
328
|
+
end
|
329
|
+
|
330
|
+
def task
|
331
|
+
config.to_h.each_with_object({}) do |(k,v), result|
|
332
|
+
result[k.to_sym] = v
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
{
|
2
|
+
"id": 35436,
|
3
|
+
"url": "https://company.zendesk.com/api/v2/tickets/35436.json",
|
4
|
+
"external_id": "ahg35h3jh",
|
5
|
+
"created_at": "2009-07-20T22:55:29Z",
|
6
|
+
"updated_at": "2011-05-05T10:38:52Z",
|
7
|
+
"type": "incident",
|
8
|
+
"subject": "Help, my printer is on fire!",
|
9
|
+
"raw_subject": "{{dc.printer_on_fire}}",
|
10
|
+
"description": "The fire is very colorful.",
|
11
|
+
"priority": "high",
|
12
|
+
"status": "open",
|
13
|
+
"recipient": "support@company.com",
|
14
|
+
"requester_id": 20978392,
|
15
|
+
"submitter_id": 76872,
|
16
|
+
"assignee_id": 235323,
|
17
|
+
"organization_id": 509974,
|
18
|
+
"group_id": 98738,
|
19
|
+
"collaborator_ids": [35334, 234],
|
20
|
+
"forum_topic_id": 72648221,
|
21
|
+
"problem_id": 9873764,
|
22
|
+
"has_incidents": false,
|
23
|
+
"due_at": null,
|
24
|
+
"tags": ["enterprise", "other_tag"],
|
25
|
+
"via": {
|
26
|
+
"channel": "web"
|
27
|
+
},
|
28
|
+
"custom_fields": [
|
29
|
+
{
|
30
|
+
"id": 27642,
|
31
|
+
"value": "745"
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"id": 27648,
|
35
|
+
"value": "yes"
|
36
|
+
}
|
37
|
+
],
|
38
|
+
"satisfaction_rating": {
|
39
|
+
"id": 1234,
|
40
|
+
"score": "good",
|
41
|
+
"comment": "Great support!"
|
42
|
+
},
|
43
|
+
"sharing_agreement_ids": [84432]
|
44
|
+
}
|