embulk-input-jira 0.0.3 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5e99c37b9c22a10ca74920b940eb2acf874d0676
4
- data.tar.gz: b85987d31a8870a6136d14dd75f6b65605160211
3
+ metadata.gz: c3c5410e3708624d8c297422cbe1e1551964fd41
4
+ data.tar.gz: b29d96f3a03d59786269c4e1799888761f3afb95
5
5
  SHA512:
6
- metadata.gz: 010bff47e3e9c3a48c24d1da78ef835450f350d7b102969e5ab89c4847425e368719009bc70511f54d2f46347f70e9235883e3e383e3c54651fbfb5a86d193be
7
- data.tar.gz: 6a99ada3fcfc2fc9d0b51484a40e85d6d51797a75964251714d9ebcc8d4f3b24f6f3e6f7af56097e329fd8647f9f7d9397587d827dfc10e1a273e658c9b32005
6
+ metadata.gz: 76b46642effb1df89e29d8a2cd7fd60ac2321dd2f0f5736fff448836d11f427239d8c829ffdd3635965a82b3e3d95b5492a4e82180cb52de3a19f92e2201e40c
7
+ data.tar.gz: 1a292c77fa20cef49500dadc2dde6929f6aa7bdf0be8f732e3e32049664c6eeb1815293443aa006b5546681fbde876817f667f90ff3f24dc5d7ee485a2d69ca8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 0.0.4 - 2015-06-22
2
+ * [enhancement] Add more schema generated by guess command [#27](https://github.com/treasure-data/embulk-input-jira/pull/27)
3
+
1
4
  ## 0.0.3 - 2015-06-12
2
5
  * [enhancement] Limit records for guess and preview [#25](https://github.com/treasure-data/embulk-input-jira/pull/25)
3
6
  * [maintenance] Define version number as constant [#24](https://github.com/treasure-data/embulk-input-jira/pull/24)
@@ -20,46 +20,112 @@ module Embulk
20
20
  return key
21
21
  end
22
22
 
23
- chunk = fields
24
- attribute.split('.').each do |key|
25
- chunk = chunk[key]
26
- return chunk if chunk.nil?
27
- end
23
+ attribute_keys = attribute.split('.')
28
24
 
29
- if chunk.is_a?(Array) || chunk.is_a?(Hash)
30
- chunk.to_json.to_s
31
- else
32
- chunk
33
- end
25
+ fetch(fields, attribute_keys)
34
26
  end
35
27
 
36
28
  def to_record
37
- record = {}
29
+ @record = {}
38
30
 
39
- record["id"] = id
40
- record["key"] = key
31
+ @record["id"] = id
32
+ @record["key"] = key
41
33
 
42
- fields.each_pair do |key, value|
43
- record_key = key
44
- record_value = value.to_json.to_s
34
+ generate_record(fields, "")
35
+
36
+ @record
37
+ end
38
+
39
+ private
40
+
41
+ def fetch(fields, keys)
42
+ return fields if fields.nil? || (fields.is_a?(Array) && fields.empty?)
43
+
44
+ if keys.empty?
45
+ case fields
46
+ when Array
47
+ values = fields.map do |field|
48
+ if field.is_a?(String)
49
+ field.to_s
50
+ else
51
+ field.to_json
52
+ end
53
+ end
45
54
 
46
- case value
47
- when String
48
- record_value = value
55
+ return values.join(",")
49
56
  when Hash
50
- if value.keys.include?("name")
51
- record_key += ".name"
52
- record_value = value["name"]
53
- elsif value.keys.include?("id")
54
- record_key += ".id"
55
- record_value = value["id"]
57
+ return fields.to_json
58
+ else
59
+ return fields
60
+ end
61
+ end
62
+
63
+ target_key = keys.shift
64
+ if fields.is_a?(Array)
65
+ values = fields.map do |field|
66
+ if field.is_a?(Hash)
67
+ field[target_key]
68
+ else
69
+ field.to_json
56
70
  end
57
71
  end
58
72
 
59
- record[record_key] = record_value
73
+ fetch(values, keys)
74
+ else
75
+ fetch(fields[target_key], keys)
60
76
  end
77
+ end
78
+
79
+ def generate_record(value, prefix_key)
80
+ case value
81
+ when Hash
82
+ # NOTE: If you want to flatten JSON completely, please
83
+ # remove this if...end and #add_heuristic_value.
84
+ if prefix_key.count(".") > 1
85
+ add_heuristic_value(value, prefix_key)
86
+ return
87
+ end
61
88
 
62
- record
89
+ value.each_pair do |_key, _value|
90
+ generate_record(_value, record_key(prefix_key, _key))
91
+ end
92
+ when Array
93
+ if value.empty? || value.any? {|v| !v.is_a?(Hash) }
94
+ @record[prefix_key] = "\"#{value.map(&:to_s).join(',')}\""
95
+ return
96
+ end
97
+
98
+ # gathering values from each Hash
99
+ keys = value.map(&:keys).inject([]) {|sum, key| sum + key }.uniq
100
+ values = value.inject({}) do |sum, elem|
101
+ keys.each {|key| sum[key] = (sum[key] || []) << elem[key] }
102
+ sum
103
+ end
104
+
105
+ generate_record(values, prefix_key)
106
+ else
107
+ @record[prefix_key] = value
108
+ end
109
+ end
110
+
111
+ def record_key(prefix, key)
112
+ return key if prefix.empty?
113
+
114
+ "#{prefix}.#{key}"
115
+ end
116
+
117
+ def add_heuristic_value(hash, prefix_key)
118
+ heuristic_values = hash.select do |key, value|
119
+ ["name", "key", "id"].include?(key) && !value.nil?
120
+ end
121
+
122
+ if heuristic_values.empty?
123
+ @record[prefix_key] = hash.to_json
124
+ else
125
+ heuristic_values.each do |key, value|
126
+ @record[record_key(prefix_key, key)] = value
127
+ end
128
+ end
63
129
  end
64
130
  end
65
131
  end
@@ -1,7 +1,7 @@
1
1
  module Embulk
2
2
  module Input
3
3
  module Jira
4
- VERSION = "0.0.3".freeze
4
+ VERSION = "0.0.4".freeze
5
5
  end
6
6
  end
7
7
  end
@@ -1,5 +1,27 @@
1
1
  # This module contains methods for Plugin.
2
2
 
3
+ module Embulk::Guess
4
+ module SchemaGuess
5
+ class << self
6
+
7
+ # NOTE: Original #from_hash_records uses keys of the first record only,
8
+ # but JSON from JIRA API has fields which of value is sometimes nil,
9
+ # sometimes JSON,
10
+ # so the first record doesn't have all key for guess always.
11
+ # original Embulk::Guess::SchemaGuess is https://github.com/embulk/embulk/blob/57b42c31d1d539177e1e818f294550cde5b69e1f/lib/embulk/guess/schema_guess.rb#L16-L24
12
+ def from_hash_records(array_of_hash)
13
+ array_of_hash = Array(array_of_hash)
14
+ if array_of_hash.empty?
15
+ raise "SchemaGuess Can't guess schema from no records"
16
+ end
17
+ column_names = array_of_hash.map(&:keys).inject([]) {|r, a| r + a }.uniq.sort
18
+ samples = array_of_hash.to_a.map {|hash| column_names.map {|name| hash[name] } }
19
+ from_array_records(column_names, samples)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
3
25
  module Embulk
4
26
  module Input
5
27
  module JiraInputPluginUtils
@@ -49,85 +49,139 @@ describe Embulk::Input::Jira::Issue do
49
49
  {
50
50
  "id" => "1",
51
51
  "jira_key" => "FOO-1",
52
- "fields" => {
53
- "summary" => "jira issue",
54
- "project" => project_attribute,
55
- "labels" =>
56
- [
57
- "Feature",
58
- "WantTo"
59
- ],
60
- "priority" => {
61
- "iconUrl" => "https://jira-api/images/icon.png",
62
- "name" => "Must",
63
- "id" => "1"
64
- },
65
- "customfield_1" => nil,
66
- }
52
+ "fields" => fields_attributes
67
53
  }
68
54
  end
69
55
 
70
- let(:project_attribute) do
71
- {
72
- "key" => "FOO",
73
- }
56
+ context "when target attribute_name is especially key" do
57
+ let(:fields_attributes) { {} }
58
+
59
+ context 'id' do
60
+ let(:attribute_name) { 'id' }
61
+
62
+ it "returns issue id" do
63
+ expect(subject).to eq "1"
64
+ end
65
+ end
66
+
67
+ context 'key' do
68
+ let(:attribute_name) { 'key' }
69
+
70
+ it "returns issue key" do
71
+ expect(subject).to eq "FOO-1"
72
+ end
73
+ end
74
74
  end
75
75
 
76
- context 'id' do
77
- let(:attribute_name) { 'id' }
76
+ context "when fields_attributes is empty Hash" do
77
+ let(:fields_attributes) { {} }
78
+
79
+ let(:attribute_name) { 'key1' }
80
+
81
+ it "returns nil" do
82
+ expect(subject).to eq nil
83
+ end
84
+ end
85
+
86
+ context "when fields_attributes is empty Hash including Array" do
87
+ let(:fields_attributes) { { 'key1' => [] } }
88
+
89
+ let(:attribute_name) { 'key1' }
78
90
 
79
- it "returns issue id" do
80
- expect(subject).to eq "1"
91
+ it "returns empty Array" do
92
+ expect(subject).to eq []
81
93
  end
82
94
  end
83
95
 
84
- context 'key' do
96
+ context "when fields_attributes is Hash" do
97
+ let(:fields_attributes) do
98
+ {'key' => 'value'}
99
+ end
100
+
85
101
  let(:attribute_name) { 'key' }
86
102
 
87
- it "returns issue key" do
103
+ it "returns 'value'" do
88
104
  expect(subject).to eq "FOO-1"
89
105
  end
90
106
  end
91
107
 
92
- context 'summary' do
93
- let(:attribute_name) { 'summary' }
108
+ context "when fields_attributes is `{'key1' => {'key2' => 'value2', 'key3' => 'value3'}}`" do
109
+ let(:fields_attributes) do
110
+ {'key1' => {'key2' => 'value2', 'key3' => 'value3'}}
111
+ end
94
112
 
95
- it "returns issue summary" do
96
- expect(subject).to eq 'jira issue'
113
+ context "when attribute_name is 'key1'" do
114
+ let(:attribute_name) { 'key1' }
115
+
116
+ it "returns key1's JSON" do
117
+ expect(subject).to eq({'key2' => 'value2', 'key3' => 'value3'}.to_json)
118
+ end
119
+ end
120
+
121
+ context "when attribute_name is 'key1.key2'" do
122
+ let(:attribute_name) { 'key1.key2' }
123
+
124
+ it "returns 'value2'" do
125
+ expect(subject).to eq 'value2'
126
+ end
97
127
  end
98
128
  end
99
129
 
100
- context 'project.key' do
101
- let(:attribute_name) { 'project.key' }
130
+ context "when fields_attributes is `{'key1' => [{'key2' => 'value2-1'}, {'key2' => 'value2-2'}]}`" do
131
+ let(:fields_attributes) do
132
+ {'key1' => [{'key2' => 'value2-1'}, {'key2' => 'value2-2'}]}
133
+ end
134
+
135
+ context "when attribute_name is 'key1'" do
136
+ let(:attribute_name) { 'key1' }
102
137
 
103
- context "when project is not nil" do
104
- it "returns issue's project key" do
105
- expect(subject).to eq 'FOO'
138
+ it "returns JSON array" do
139
+ expect(subject).to eq '{"key2":"value2-1"},{"key2":"value2-2"}'
106
140
  end
107
141
  end
108
142
 
109
- context "when project is nil" do
110
- let(:project_attribute) { nil }
143
+ context "when attribute_name is 'key1.key2'" do
144
+ let(:attribute_name) { 'key1.key2' }
111
145
 
112
- it "returns nil" do
113
- expect(subject).to be_nil
146
+ it "returns CSV values assigned by 'key2' key" do
147
+ expect(subject).to eq 'value2-1,value2-2'
114
148
  end
115
149
  end
116
150
  end
117
151
 
118
- context 'labels' do
119
- let(:attribute_name) { 'labels' }
152
+ context "when fields_attributes is `{'key1' => [{'key2' => 'value2-1'}, nil]}`" do
153
+ let(:fields_attributes) do
154
+ {'key1' => [{'key2' => 'value2-1'}, nil]}
155
+ end
156
+
157
+ context "when attribute_name is 'key1'" do
158
+ let(:attribute_name) { 'key1' }
159
+
160
+ it "returns CSV value including null" do
161
+ expect(subject).to eq '{"key2":"value2-1"},null'
162
+ end
163
+ end
164
+
165
+ context "when attribute_name is 'key1.key2'" do
166
+ let(:attribute_name) { 'key1.key2' }
120
167
 
121
- it "returns issue's labels JSON string" do
122
- expect(subject).to eq '["Feature","WantTo"]'
168
+ it "returns JSON array including null" do
169
+ expect(subject).to eq 'value2-1,null'
170
+ end
123
171
  end
124
172
  end
125
173
 
126
- context 'priority' do
127
- let(:attribute_name) { 'priority' }
174
+ context "when fields_attributes is `{'key1' => ['element1', 'element2', 'element3']}`" do
175
+ let(:fields_attributes) do
176
+ {
177
+ 'key1' => ['element1', 'element2', 'element3'],
178
+ }
179
+ end
180
+
181
+ let(:attribute_name) { 'key1' }
128
182
 
129
- it "returns issue's priority JSON string" do
130
- expect(subject).to eq '{"iconUrl":"https://jira-api/images/icon.png","name":"Must","id":"1"}'
183
+ it "returns CSV values assigned by 'key1'" do
184
+ expect(subject).to eq 'element1,element2,element3'
131
185
  end
132
186
  end
133
187
  end
@@ -137,44 +191,204 @@ describe Embulk::Input::Jira::Issue do
137
191
  Embulk::Input::Jira::Issue.new(issue_attributes).to_record
138
192
  end
139
193
 
194
+ shared_examples 'return guessed record' do
195
+ it do
196
+ expect(subject).to eq expected_record
197
+ end
198
+ end
199
+
140
200
  let(:issue_attributes) do
201
+ {"jira_key" => "FOO-1", "id" => "1", "fields" => fields_attributes}
202
+ end
203
+
204
+ let(:expected_record) do
141
205
  {
142
- "id" => 1,
143
- "jira_key" => "FOO-1",
144
- "fields" => {
145
- "summary" => "jira issue",
146
- "project" => {
147
- "id" => "FOO",
148
- },
149
- "labels" =>
206
+ "key" => "FOO-1",
207
+ "id" => "1"
208
+ }.merge(exptected_record_from_fields)
209
+ end
210
+
211
+ context "when fields_attributes is `{'key' => 'value'}`" do
212
+ let(:fields_attributes) do
213
+ {'key' => 'value'}
214
+ end
215
+
216
+ let(:exptected_record_from_fields) do
217
+ {'key' => 'value'}
218
+ end
219
+
220
+ it_behaves_like "return guessed record"
221
+ end
222
+
223
+ context "when fields_attributes is `{'key1' => {'key2' => 'value2', 'key3' => 'value3'}}`" do
224
+ let(:fields_attributes) do
225
+ {
226
+ 'key1' => {
227
+ 'key2' => 'value2',
228
+ 'key3' => 'value3',
229
+ }
230
+ }
231
+ end
232
+
233
+ let(:exptected_record_from_fields) do
234
+ {
235
+ "key1.key2" => "value2",
236
+ "key1.key3" => "value3"
237
+ }
238
+ end
239
+
240
+ it_behaves_like "return guessed record"
241
+ end
242
+
243
+ context "when fields_attributes is `{'key1' => {'key2' => {'key3' => last_child}}}`" do
244
+ let(:fields_attributes) do
245
+ {'key1' => {'key2' => {'key3' => last_child}}}
246
+ end
247
+
248
+ context "when last_child is String" do
249
+ let(:last_child) do
250
+ "String"
251
+ end
252
+
253
+ let(:exptected_record_from_fields) do
254
+ {"key1.key2.key3" => "String"}
255
+ end
256
+
257
+ it_behaves_like "return guessed record"
258
+ end
259
+
260
+ context "when last_child has 'key' key" do
261
+ let(:last_child) do
262
+ {"key" => "BAR-1"}
263
+ end
264
+
265
+ let(:exptected_record_from_fields) do
266
+ {"key1.key2.key3.key" => "BAR-1"}
267
+ end
268
+
269
+ it_behaves_like "return guessed record"
270
+ end
271
+
272
+ context "when last_child has 'id' key" do
273
+ let(:last_child) do
274
+ {"id" => "20"}
275
+ end
276
+
277
+ let(:exptected_record_from_fields) do
278
+ {"key1.key2.key3.id" => "20"}
279
+ end
280
+
281
+ it_behaves_like "return guessed record"
282
+ end
283
+
284
+ context "when last_child has 'name' key" do
285
+ let(:last_child) do
286
+ {"name" => "Foo name"}
287
+ end
288
+
289
+ let(:exptected_record_from_fields) do
290
+ {"key1.key2.key3.name" => "Foo name"}
291
+ end
292
+
293
+ it_behaves_like "return guessed record"
294
+ end
295
+
296
+ context "when last_child has another key except 'key', 'id', 'name'" do
297
+ let(:last_child) do
298
+ {"customfield_0001" => "value0001"}
299
+ end
300
+
301
+ let(:exptected_record_from_fields) do
302
+ {"key1.key2.key3" => "{\"customfield_0001\":\"value0001\"}"}
303
+ end
304
+
305
+ it_behaves_like "return guessed record"
306
+ end
307
+
308
+ context "when last_child Hash Array" do
309
+ let(:last_child) do
150
310
  [
151
- "Feature",
152
- "WantTo"
153
- ],
154
- "priority" => {
155
- "iconUrl" => "https://jira-api/images/icon.png",
156
- "name" => "Must",
157
- "id" => "1"
158
- },
159
- "customfield_1" => nil,
311
+ {"key4" => "value4", "key5" => "value5-1"},
312
+ {"key6" => "value6", "key5" => "value5-2"},
313
+ ]
314
+ end
315
+
316
+ let(:exptected_record_from_fields) do
317
+ {"key1.key2.key3" => '{"key4":["value4",null],"key5":["value5-1","value5-2"],"key6":[null,"value6"]}'}
318
+ end
319
+
320
+ it_behaves_like "return guessed record"
321
+ end
322
+ end
323
+
324
+ context "when fields_attributes is `{'key1' => ['element1-1', 'element1-2', 'element1-3'], 'key2' => ['element2-1', 'element2-2', 'element2-3']}`" do
325
+ let(:fields_attributes) do
326
+ {
327
+ 'key1' => ['element1-1', 'element1-2', 'element1-3'],
328
+ 'key2' => ['element2-1', 'element2-2', 'element2-3'],
160
329
  }
161
- }
330
+ end
331
+
332
+ let(:exptected_record_from_fields) do
333
+ {
334
+ 'key1' => '"element1-1,element1-2,element1-3"',
335
+ 'key2' => '"element2-1,element2-2,element2-3"',
336
+ }
337
+ end
338
+
339
+ it_behaves_like "return guessed record"
162
340
  end
163
341
 
164
- let(:expected) do
165
- {
166
- "id" => 1,
167
- "key" => "FOO-1",
168
- "summary" => "jira issue",
169
- "project.id" => "FOO",
170
- "labels" => "[\"Feature\",\"WantTo\"]",
171
- "priority.name" => "Must",
172
- "customfield_1" => "null"
173
- }
342
+ context "when fields_attributes is `{'key' => []}`" do
343
+ let(:fields_attributes) do
344
+ {'key' => []}
345
+ end
346
+
347
+ let(:exptected_record_from_fields) do
348
+ {"key" => '""'}
349
+ end
350
+
351
+ it_behaves_like "return guessed record"
174
352
  end
175
353
 
176
- it 'return guessed record' do
177
- expect(subject).to eq expected
354
+ context "when fields_attributes is `{'key1' => { 'key2' => [{'key3' => 'value3-1'}, {'key3'=> 'value3-2'}]}}`" do
355
+ let(:fields_attributes) do
356
+ {
357
+ 'key1' => {
358
+ 'key2' => [
359
+ {'key3' => 'value3-1'}, {'key3'=> 'value3-2'},
360
+ ]
361
+ }
362
+ }
363
+ end
364
+
365
+ let(:exptected_record_from_fields) do
366
+ {
367
+ "key1.key2.key3" => '"value3-1,value3-2"',
368
+ }
369
+ end
370
+
371
+ it_behaves_like "return guessed record"
372
+ end
373
+
374
+ context "when fields_attributes is `{'key1' => { 'key2' => ['element2-1', 'element2-2', 'element2-3'], 'key3' => ['element3-1', 'element3-2', 'element3-3']}}`" do
375
+ let(:fields_attributes) do
376
+ {
377
+ 'key1' => {
378
+ 'key2' => ['element2-1', 'element2-2', 'element2-3'],
379
+ 'key3' => ['element3-1', 'element3-2', 'element3-3']
380
+ }
381
+ }
382
+ end
383
+
384
+ let(:exptected_record_from_fields) do
385
+ {
386
+ "key1.key2" => '"element2-1,element2-2,element2-3"',
387
+ "key1.key3" => '"element3-1,element3-2,element3-3"',
388
+ }
389
+ end
390
+
391
+ it_behaves_like "return guessed record"
178
392
  end
179
393
  end
180
394
  end
@@ -8,15 +8,17 @@ describe Embulk::Input::JiraInputPluginUtils do
8
8
 
9
9
  let(:records) do
10
10
  [
11
- {"project_key" => "FOO", "comment.total" => 3, "created" => "2015-03-01T00:12:00"}
11
+ {"project.key" => "FOO-1", "comment.total" => 3, "created" => "2015-03-01T00:12:00"},
12
+ {"project.id" => "1", "project.key" => "FOO", "comment.total" => 3, "created" => "2015-03-01T00:12:00"}
12
13
  ]
13
14
  end
14
15
 
15
16
  let(:expected) do
16
17
  [
17
- {name: "project_key", type: :string},
18
18
  {name: "comment.total", type: :long},
19
- {name: "created", type: :timestamp, format: "%Y-%m-%dT%H:%M:%S"}
19
+ {name: "created", type: :timestamp, format: "%Y-%m-%dT%H:%M:%S"},
20
+ {name: "project.id", type: :long},
21
+ {name: "project.key", type: :string},
20
22
  ]
21
23
  end
22
24
 
@@ -116,10 +116,12 @@ describe Embulk::Input::JiraInputPlugin do
116
116
  let(:guessed_config) do
117
117
  {
118
118
  "columns" => [
119
+ {name: "comment.comments", type: :string},
120
+ {name: "comment.total", type: :long},
119
121
  {name: "id", type: :long},
120
122
  {name: "key", type: :string},
123
+ {name: "project.key", type: :string},
121
124
  {name: "project.name", type: :string},
122
- {name: "comment", type: :string}
123
125
  ]
124
126
  }
125
127
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-input-jira
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - uu59
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-06-12 00:00:00.000000000 Z
12
+ date: 2015-06-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  requirement: !ruby/object:Gem::Requirement