embulk-input-zendesk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.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
|
+
}
|