embulk-input-jira 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c300bb312a6bbf2bfa30a0ed5531e7c3a70a7c4f
4
+ data.tar.gz: 1cd0319e3794bbde87d9fb82b1d4c9833bb05678
5
+ SHA512:
6
+ metadata.gz: 73cb22cdbf30f9a43b4d3c3a42d3487064581cb56a6af3fc7c4b3dbd4cd700c5c89979126cbc712da38b1ea588bb6ff0d5ad61675c840e62f8694bb67c73d37f
7
+ data.tar.gz: 5ad6cf1fbcef9d78c22a1b21f03df1818d856f0a3803cf339aaea0ccf4fbb9bb640c8b30100b5993b3c963eea68ab99d5d2af3ba7e6e8a19f31972ddd6aa2dc3
@@ -0,0 +1,7 @@
1
+ *~
2
+ /pkg/
3
+ /tmp/
4
+ /.bundle/
5
+ /Gemfile.lock
6
+ /.ruby-version
7
+ /coverage/
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - jruby-19mode
4
+ env:
5
+ global:
6
+ secure: "NCkU3f60cn5Gmzq+NwDmKdQSq+ApF/rPqSHPirk1+ZPW9n+H9912meUuJr9qyn03MPdc6wAQinsl3skdPNh1Qz02TI/WZGMiCQTLh3nKIMzaaxx6I+OZ1TV7A4MoJMQ7oYQdXdbvRYXpOVRnfVDxpUC+BDk2T8xmjfm9cht+PGTzM6RxpmSL842hiudkcyxHxgEf66EGpi6h2G2PqYNfQrksqiVMWlLiTO5a2ee+iw9qTMhgKrhEKLCb+dFB56gtKIOpd3MxieLwf3PDcsjM+TSaWo6Bp+mjdA3zGJNz5wbWRVSdrIvKvxmf3eRqAGssBhBDO/LUN3LnI+/T/7J9XZZd7WXNudo/t6HQXhJkUIvOucCxSg8/uRsxrMAmF2zkJOnYGSvN3HNqdzyz86TK4/Xckl+gX51b0uYncHfZYSojn4py1RWPYpX/L2RHbzMn1MJ7B3/VZ21vY97aHhDgNyyAMxPnTAuR1IZicEgA8y8AmZkkK9tWq5wvAOKI91V+W8iBEaO2dZIib+KdJSUUj/Q3TM3i4wX9O/AU7tknsnCApvxQeveELJ4SxfFd/Z2imqmQWM0wIgp1u7Vjguf8kSaxcuQHonZNdwvvYOkbYxHvUqltzlmE1qFC/kQe6o9fS3NuiugHCYVeFs+LXVVW+nl0pWztFYMicUmQq7T781w="
7
+ notifications:
8
+ slack:
9
+ secure: QouVFiuH4dTRpjUExfu4NqOYCmt53rmchedTbXqsDXOuLoUbGCMHJgoaYnxJn4Gltqydp8dHXJTE+1hBHZTCGqvO4gng5Z66N7r2DaUk15+mCJPcRl15aMX6J5bXvMp5M7xZYd4AhmB4b3RsdsF4qyH1rf2k9IhakQX09xj1C1OjO3SvMDT/PSQ3cV4mVs4zltYBmNeln9J5rZNexHBg/Mbwz1IqPnR0In5vEUhwBwRc3eo7JoxjBR5/3ffVJcLG1uf9askuW3YacR4XVMYXzFlUErS9QHyugSNTac03YpJmfg8YtY2H1o5ScicwMda3T0nKixHff/Ftkwh7DNsji0edW1WVk+Zt+0P/8ULj/k5gS4uhCERaXNFqRovwqI7is2I1rQFs3TQAQynmXyT0y/jHF0YspTStvuUjSFvuX4P0yaeX39Flih8ZM/js+TkKWjunFsBueqLzcDIjaJ2DFRPi1xDkmwayBhZEpECUE5R19170QsOyIbiG4NFXWWbDIUIapFZKoiohIp46J+NqWYu0Hp/TN6wcqPW7bXVamMma28koM1BLVajW/Ikknqg3Yzf7pzKkRxmoda/fkdfMkr4pqH056YIVKeHKRbrHnfEBArG++HW/T+L8GBW8wH+9fJVeu0G1VsSFH57YARIyO3WF0eqpHB6HHySn2p+DM3Y=
10
+ jdk:
11
+ - oraclejdk8
@@ -0,0 +1,3 @@
1
+ ## 0.0.1 (2015-05-28)
2
+
3
+ The first release!!
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org/'
2
+
3
+ # NOTE: Bundler.require(:runtime, :development) in spec_helper that require all dependencies
4
+ # but require 'embulk' will be failed. so separate it from gemspec ecosystem
5
+ gem 'embulk', ">= 0.6", "< 1.0"
6
+ gem "codeclimate-test-reporter", require: nil
7
+
8
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2015 Everyleaf Corporation
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,45 @@
1
+ [![Build Status](https://travis-ci.org/treasure-data/embulk-input-jira.svg)](https://travis-ci.org/treasure-data/embulk-input-jira)
2
+ [![Code Climate](https://codeclimate.com/github/treasure-data/embulk-input-jira/badges/gpa.svg)](https://codeclimate.com/github/treasure-data/embulk-input-jira)
3
+ [![Test Coverage](https://codeclimate.com/github/treasure-data/embulk-input-jira/badges/coverage.svg)](https://codeclimate.com/github/treasure-data/embulk-input-jira/coverage)
4
+
5
+ # Jira input plugin for Embulk
6
+
7
+ embulk-input-jira is the Embulk input plugin for [JIRA](https://www.atlassian.com/software/jira).
8
+
9
+ ## Overview
10
+
11
+ * **Plugin type**: input
12
+ * **Resume supported**: no
13
+ * **Cleanup supported**: no
14
+ * **Guess supported**: yes
15
+
16
+ ## Configuration
17
+
18
+ - **username** JIRA username (string, required)
19
+ - **password** JIRA password (string, required)
20
+ - **uri** JIRA API endpoint (string, required)
21
+ - **jql** [JQL](https://confluence.atlassian.com/display/JIRA/Advanced+Searching) for extract target issues (string, required)
22
+ - **columns** target issue attributes. You can generate this configuration by `guess` command (array, required)
23
+
24
+ ## Example
25
+
26
+ ```yaml
27
+ in:
28
+ type: jira
29
+ username: USERNAME
30
+ password: PASSWORD
31
+ uri: http://localhost:8090
32
+ jql: project = PRO AND summary~Fix
33
+ columns:
34
+ - {name: id, type: long}
35
+ - {name: key, type: string}
36
+ - {name: project.name, type: string}
37
+ - {name: summary, type: string}
38
+ - {name: assignee.name, type: string}
39
+ ```
40
+
41
+ ## Build
42
+
43
+ ```
44
+ $ bundle exec rake build
45
+ ```
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ task default: :spec
5
+
6
+ desc "Run all examples"
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.rspec_opts = %w[--color]
9
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,22 @@
1
+
2
+ Gem::Specification.new do |spec|
3
+ spec.name = "embulk-input-jira"
4
+ spec.version = File.read(File.expand_path("../VERSION", __FILE__)).strip
5
+ spec.authors = ["uu59", "yoshihara"]
6
+ spec.summary = "Jira input plugin for Embulk"
7
+ spec.description = "Loads records from Jira."
8
+ spec.email = ["k@uu59.org", "h.yoshihara@everyleaf.com"]
9
+ spec.licenses = ["Apache2"]
10
+ spec.homepage = "https://github.com/treasure-data/embulk-input-jira"
11
+
12
+ spec.files = `git ls-files`.split("\n") + Dir["classpath/*.jar"]
13
+ spec.test_files = spec.files.grep(%r{^(test|spec)/})
14
+ spec.require_paths = ["lib"]
15
+
16
+ spec.add_dependency 'jiralicious', ['~> 0.5.0']
17
+ spec.add_development_dependency 'bundler', ['~> 1.0']
18
+ spec.add_development_dependency 'rake', ['>= 10.0']
19
+ spec.add_development_dependency 'rspec', "~> 3.2.0"
20
+ spec.add_development_dependency 'simplecov'
21
+ spec.add_development_dependency 'pry'
22
+ end
@@ -0,0 +1,36 @@
1
+ # This module contains methods for Plugin.
2
+
3
+ module Embulk
4
+ module Input
5
+ module JiraInputPluginUtils
6
+ # Guess::SchemaGuess.from_hash_records returns Columns
7
+ # containing 'index' key, but it is needless.
8
+ def self.guess_columns(records)
9
+ schema = Guess::SchemaGuess.from_hash_records(records)
10
+
11
+ schema.map do |c|
12
+ column = {name: c.name, type: c.type}
13
+ column[:format] = c.format if c.format
14
+ column
15
+ end
16
+ end
17
+
18
+ def self.cast(value, type)
19
+ return value if value.nil?
20
+
21
+ case type.to_sym
22
+ when :long
23
+ Integer(value)
24
+ when :double
25
+ Float(value)
26
+ when :timestamp
27
+ Time.parse(value)
28
+ when :boolean
29
+ !!value
30
+ else
31
+ value.to_s
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,113 @@
1
+ require "embulk/input/jira-input-plugin-utils"
2
+ require "jira/api"
3
+
4
+ module Embulk
5
+ module Input
6
+ class JiraInputPlugin < InputPlugin
7
+ PER_PAGE = 50
8
+
9
+ Plugin.register_input("jira", self)
10
+
11
+ def self.version
12
+ File.read(File.expand_path("../../../../VERSION", __FILE__)).strip
13
+ end
14
+
15
+ def self.transaction(config, &control)
16
+ task = {
17
+ "username" => config.param("username", :string),
18
+ "password" => config.param("password", :string),
19
+ "uri" => config.param("uri", :string),
20
+ "jql" => config.param("jql", :string),
21
+ }
22
+
23
+ attributes = {}
24
+ columns = config.param("columns", :array).map do |column|
25
+ name = column["name"]
26
+ type = column["type"].to_sym
27
+ attributes[name] = type
28
+ Column.new(nil, name, type, column["format"])
29
+ end
30
+
31
+ task["attributes"] = attributes
32
+
33
+ resume(task, columns, 1, &control)
34
+ end
35
+
36
+ def self.resume(task, columns, count, &control)
37
+ commit_reports = yield(task, columns, count)
38
+
39
+ next_config_diff = {}
40
+ return next_config_diff
41
+ end
42
+
43
+ def self.guess(config)
44
+ # TODO: api_version should be 2 (the latest version)
45
+ # auth_type should be specified from config. (The future task)
46
+
47
+ username = config.param("username", :string)
48
+ password = config.param("password", :string)
49
+ uri = config.param("uri", :string)
50
+ api_version = "latest"
51
+ auth_type = "basic"
52
+ jql = config.param("jql", :string)
53
+
54
+ jira = Jira::Api.setup do |jira_config|
55
+ jira_config.username = username
56
+ jira_config.password = password
57
+ jira_config.uri = uri
58
+ jira_config.api_version = api_version
59
+ jira_config.auth_type = auth_type
60
+ end
61
+
62
+ # TODO: we use 0..10 issues to guess config?
63
+ records = jira.search_issues(jql)[0..10].map do |issue|
64
+ issue.to_record
65
+ end
66
+
67
+ columns = JiraInputPluginUtils.guess_columns(records)
68
+
69
+ guessed_config = {
70
+ "username" => username,
71
+ "password" => password,
72
+ "uri" => uri,
73
+ "api_version" => api_version,
74
+ "auth_type" => auth_type,
75
+ "columns" => columns,
76
+ }
77
+
78
+ return guessed_config
79
+ end
80
+
81
+ def init
82
+ @attributes = task["attributes"]
83
+ @jira = Jira::Api.setup do |config|
84
+ config.username = task["username"]
85
+ config.password = task["password"]
86
+ config.uri = task["uri"]
87
+ config.api_version = "latest"
88
+ config.auth_type = :basic
89
+ end
90
+ @jql = task["jql"]
91
+ end
92
+
93
+ def run
94
+ total_count = @jira.total_count(@jql)
95
+
96
+ 0.step(total_count, PER_PAGE) do |start_at|
97
+ @jira.search_issues(@jql, start_at: start_at).each do |issue|
98
+ values = @attributes.map do |(attribute_name, type)|
99
+ JiraInputPluginUtils.cast(issue[attribute_name], type)
100
+ end
101
+
102
+ page_builder.add(values)
103
+ end
104
+ end
105
+
106
+ page_builder.finish
107
+
108
+ commit_report = {}
109
+ return commit_report
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,25 @@
1
+ require "jiralicious"
2
+ require "jira/issue"
3
+
4
+ module Jira
5
+ class Api
6
+ def self.setup(&block)
7
+ Jiralicious.configure(&block)
8
+ new
9
+ end
10
+
11
+ def search_issues(jql, options={})
12
+ search(jql, options).issues.map do |issue|
13
+ ::Jira::Issue.new(issue)
14
+ end
15
+ end
16
+
17
+ def search(jql, options={})
18
+ Jiralicious.search(jql, options)
19
+ end
20
+
21
+ def total_count(jql)
22
+ search(jql, max_results: 1).num_results
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,63 @@
1
+ module Jira
2
+ class Issue
3
+ attr_reader :id, :key, :fields
4
+
5
+ def initialize(attributes)
6
+ @id = attributes.fetch("id")
7
+
8
+ # https://github.com/dorack/jiralicious/blob/404b7b6d5b7020f42064cf8d7a745ab02057e728/lib/jiralicious/issue.rb#L11-L12
9
+ @key = attributes.fetch("jira_key")
10
+ @fields = attributes.fetch("fields")
11
+ end
12
+
13
+ def [](attribute)
14
+ case attribute
15
+ when "id"
16
+ return id
17
+ when "key"
18
+ return key
19
+ end
20
+
21
+ chunk = fields
22
+ attribute.split('.').each do |key|
23
+ chunk = chunk[key]
24
+ return chunk if chunk.nil?
25
+ end
26
+
27
+ if chunk.is_a?(Array) || chunk.is_a?(Hash)
28
+ chunk.to_json.to_s
29
+ else
30
+ chunk
31
+ end
32
+ end
33
+
34
+ def to_record
35
+ record = {}
36
+
37
+ record["id"] = id
38
+ record["key"] = key
39
+
40
+ fields.each_pair do |key, value|
41
+ record_key = key
42
+ record_value = value.to_json.to_s
43
+
44
+ case value
45
+ when String
46
+ record_value = value
47
+ when Hash
48
+ if value.keys.include?("name")
49
+ record_key += ".name"
50
+ record_value = value["name"]
51
+ elsif value.keys.include?("id")
52
+ record_key += ".id"
53
+ record_value = value["id"]
54
+ end
55
+ end
56
+
57
+ record[record_key] = record_value
58
+ end
59
+
60
+ record
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,88 @@
1
+ require "spec_helper"
2
+ require "embulk/input/jira-input-plugin-utils"
3
+
4
+ describe Embulk::Input::JiraInputPluginUtils do
5
+ describe ".guess_columns" do
6
+ subject do
7
+ Embulk::Input::JiraInputPluginUtils.guess_columns(records)
8
+ end
9
+
10
+ let(:records) do
11
+ [
12
+ {"project_key" => "FOO", "comment.total" => 3, "created" => "2015-03-01T00:12:00"}
13
+ ]
14
+ end
15
+
16
+ let(:expected) do
17
+ [
18
+ {name: "project_key", type: :string},
19
+ {name: "comment.total", type: :long},
20
+ {name: "created", type: :timestamp, format: "%Y-%m-%dT%H:%M:%S"}
21
+ ]
22
+ end
23
+
24
+ it "returns Array containing columns without 'index' key from each record" do
25
+ expect(subject).to eq expected
26
+ end
27
+ end
28
+
29
+ describe ".cast" do
30
+ subject do
31
+ Embulk::Input::JiraInputPluginUtils.cast(value, type)
32
+ end
33
+
34
+ context "when value is nil" do
35
+ let(:value) { nil }
36
+ let(:type) { :string }
37
+
38
+ it "returns nil" do
39
+ expect(subject).to be_nil
40
+ end
41
+ end
42
+
43
+ context "when value is not nil" do
44
+ let(:value) { 123 }
45
+
46
+ context "and type is :string" do
47
+ let(:type) { :string }
48
+
49
+ it "returns '123'" do
50
+ expect(subject).to eq "123"
51
+ end
52
+ end
53
+
54
+ context "and type is :long" do
55
+ let(:type) { :long }
56
+
57
+ it "returns 123" do
58
+ expect(subject).to eq 123
59
+ end
60
+ end
61
+
62
+ context "and type is :double" do
63
+ let(:type) { :double }
64
+
65
+ it "returns 123.0" do
66
+ expect(subject).to eq 123.0
67
+ end
68
+ end
69
+
70
+ context "and type is :timestamp" do
71
+ let(:value) { "2015-03-01T00:12:00" }
72
+ let(:type) { :timestamp }
73
+
74
+ it "returns Time object" do
75
+ expect(subject).to eq Time.new(2015, 3, 1, 0, 12, 0)
76
+ end
77
+ end
78
+
79
+ context "and type is :boolean" do
80
+ let(:type) { :boolean }
81
+
82
+ it "returns true" do
83
+ expect(subject).to be true
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,247 @@
1
+ require "spec_helper"
2
+ require "embulk/input/jira"
3
+
4
+ describe Embulk::Input::JiraInputPlugin do
5
+ let(:username) { "jira-user" }
6
+ let(:password) { "password" }
7
+ let(:uri) { "http://jira.example/" }
8
+ let(:jql) { "PROJECT=#{project_name}" }
9
+ let(:project_name) { "FOO" }
10
+
11
+ describe ".transaction" do
12
+ subject { Embulk::Input::JiraInputPlugin.transaction(config, &control) }
13
+
14
+ let(:config) { Object.new } # add mock later
15
+ let(:control) { Proc.new{|task, columns, count| } } # do nothing
16
+
17
+ let(:task) do
18
+ {
19
+ "username" => username,
20
+ "password" => password,
21
+ "uri" => uri,
22
+ "jql" => jql,
23
+ "attributes" => {
24
+ "project.key" => :string,
25
+ "comment.total" => :long
26
+ }
27
+ }
28
+ end
29
+
30
+ let(:columns) do
31
+ [
32
+ {"name" => "project.key", "type" => "string"},
33
+ {"name" => "comment.total", "type" => "long"}
34
+ ]
35
+ end
36
+
37
+ let(:column_structs) do
38
+ [
39
+ Embulk::Column.new(nil, "project.key", :string),
40
+ Embulk::Column.new(nil, "comment.total", :long)
41
+ ]
42
+ end
43
+
44
+ before do
45
+ allow(config).to receive(:param).with("username", :string).and_return(username)
46
+ allow(config).to receive(:param).with("password", :string).and_return(password)
47
+ allow(config).to receive(:param).with("uri", :string).and_return(uri)
48
+ allow(config).to receive(:param).with("jql", :string).and_return(jql)
49
+ allow(config).to receive(:param).with("columns", :array).and_return(columns)
50
+ end
51
+
52
+ # NOTE: I should check other factor, but i don't know it...
53
+ it "calls .resume method with proper parameters" do
54
+ expect(Embulk::Input::JiraInputPlugin).to receive(:resume).with(task, column_structs, 1, &control)
55
+ subject
56
+ end
57
+ end
58
+
59
+ describe ".resume" do
60
+ subject { Embulk::Input::JiraInputPlugin.resume(task, columns, count, &control) }
61
+
62
+ let(:task) do
63
+ {
64
+ "username" => username,
65
+ "password" => password,
66
+ "uri" => uri,
67
+ "jql" => jql,
68
+ "attributes" => {
69
+ "project.key" => :string,
70
+ "comment.total" => :long
71
+ }
72
+ }
73
+ end
74
+
75
+ let(:columns) do
76
+ [
77
+ Embulk::Column.new(nil, "project.key", :string),
78
+ Embulk::Column.new(nil, "comment.total", :long)
79
+ ]
80
+ end
81
+
82
+ let(:count) { 1 }
83
+ let(:control) { Proc.new{|task, columns, count| } } # do nothing
84
+
85
+ let(:next_config_diff) { {} }
86
+
87
+ it "returns next_config_diff" do
88
+ expect(subject).to eq next_config_diff
89
+ end
90
+ end
91
+
92
+ describe ".guess" do
93
+ subject { Embulk::Input::JiraInputPlugin.guess(config) }
94
+
95
+ let(:config) { Object.new } # add mock later
96
+
97
+ let(:jira_api) { Jira::Api.new }
98
+ let(:jira_issues) { [Jira::Issue.new(attributes)] }
99
+ let(:attributes) do
100
+ {
101
+ "id" => "100",
102
+ "jira_key" => "FOO-100",
103
+ "fields" =>
104
+ {
105
+ "project" => {
106
+ "name" => project_name,
107
+ "key" => project_name,
108
+ },
109
+ "comment" => {
110
+ "total" => 0,
111
+ "comments" => []
112
+ }
113
+ }
114
+ }
115
+ end
116
+
117
+ let(:guessed_config) do
118
+ {
119
+ "username" => username,
120
+ "password" => password,
121
+ "uri" => uri,
122
+ "api_version" => "latest",
123
+ "auth_type" => "basic",
124
+ "columns" => [
125
+ {name: "id", type: :long},
126
+ {name: "key", type: :string},
127
+ {name: "project.name", type: :string},
128
+ {name: "comment", type: :string}
129
+ ]
130
+ }
131
+ end
132
+
133
+ before do
134
+ allow(jira_api).to receive(:search_issues).and_return(jira_issues)
135
+
136
+ allow(config).to receive(:param).with("username", :string).and_return(username)
137
+ allow(config).to receive(:param).with("password", :string).and_return(password)
138
+ allow(config).to receive(:param).with("uri", :string).and_return(uri)
139
+ allow(config).to receive(:param).with("jql", :string).and_return(jql)
140
+ end
141
+
142
+ it "setup Jira::Api" do
143
+ expect(Jira::Api).to receive(:setup).and_return(jira_api)
144
+ subject
145
+ end
146
+
147
+ it "returns guessed config" do
148
+ allow(Jira::Api).to receive(:setup).and_return(jira_api)
149
+
150
+ expect(subject).to eq guessed_config
151
+ end
152
+ end
153
+
154
+ describe "#init (.new)" do
155
+ # NOTE: InputPlugin.initialize calls #init method.
156
+
157
+ subject { Embulk::Input::JiraInputPlugin.new({}, nil, nil, nil) }
158
+
159
+ it "setup Jira::Api" do
160
+ expect(Jira::Api).to receive(:setup)
161
+ subject
162
+ end
163
+
164
+ it "is a Embulk::InputPlugin" do
165
+ allow(Jira::Api).to receive(:setup)
166
+ expect(subject).to be_a(Embulk::InputPlugin)
167
+ end
168
+ end
169
+
170
+ describe "#run" do
171
+ subject do
172
+ Embulk::Input::JiraInputPlugin.new(task, nil, nil, page_builder).run
173
+ end
174
+
175
+ let(:jira_api) { Jira::Api.new }
176
+ let(:jira_issues) do
177
+ (1..total_count).map do |i|
178
+ attributes = fields.merge("id" => i.to_s, "jira_key" => "FOO-#{i}")
179
+
180
+ Jira::Issue.new(attributes)
181
+ end
182
+ end
183
+
184
+ let(:total_count) { max_result + 10 }
185
+ let(:max_result) { Embulk::Input::JiraInputPlugin::PER_PAGE }
186
+
187
+
188
+ let(:page_builder) { Object.new } # add mock later
189
+ let(:task) do
190
+ {
191
+ "jql" => jql,
192
+ "attributes" => {"project.key" => "string"}
193
+ }
194
+ end
195
+
196
+ let(:fields) do
197
+ {
198
+ "fields" =>
199
+ {
200
+ "project" => {
201
+ "key" => project_name,
202
+ },
203
+ }
204
+ }
205
+ end
206
+
207
+ let(:commit_report) { {} }
208
+
209
+ before do
210
+ # TODO: create stubs without each `it` expected
211
+ allow(Jira::Api).to receive(:setup).and_return(jira_api)
212
+
213
+ 0.step(total_count, max_result) do |start_at|
214
+ issues = jira_issues[start_at..(start_at + max_result - 1)]
215
+ allow(jira_api).to receive(:search_issues).with(jql, start_at: start_at).and_return(issues)
216
+ end
217
+ allow(jira_api).to receive(:total_count).and_return(total_count)
218
+
219
+ allow(page_builder).to receive(:add).with([project_name])
220
+ allow(page_builder).to receive(:finish)
221
+ end
222
+
223
+ it 'search JIRA issues' do
224
+ expect(jira_api).to receive(:search_issues)
225
+ subject
226
+ end
227
+
228
+ it 'page build and finish' do
229
+ expect(page_builder).to receive(:add).with([project_name]).exactly(total_count).times
230
+ expect(page_builder).to receive(:finish)
231
+ subject
232
+ end
233
+
234
+ it 'returns commit report' do
235
+ expect(subject).to eq commit_report
236
+ end
237
+ end
238
+
239
+ describe ".version" do
240
+ let(:version_file_path) { File.expand_path("../../../../VERSION", __FILE__) }
241
+ subject { Embulk::Input::JiraInputPlugin.version }
242
+
243
+ it "returns VERSION file content without line-break" do
244
+ expect(subject).to eq File.read(version_file_path).strip
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,30 @@
1
+ require "spec_helper"
2
+
3
+ describe Embulk do
4
+ describe "registerd jira as input plugin" do
5
+ let(:config) { <<-YAML }
6
+ in:
7
+ type: jira
8
+ YAML
9
+ let(:config_file) { Tempfile.new("embulk-conf") }
10
+
11
+ before do
12
+ config_file.puts config
13
+ config_file.close
14
+ end
15
+
16
+ subject {
17
+ capture_output(:out) do
18
+ Embulk.run ["preview", config_file.path]
19
+ end
20
+ }
21
+
22
+ it do
23
+ # NOTE: can't stub `exit` so stubbing `raise`
24
+ # https://github.com/embulk/embulk/blob/v0.6.9/lib/embulk/command/embulk_run.rb#L335
25
+ allow(Embulk).to receive(:raise)
26
+
27
+ is_expected.to_not include("InputPlugin 'jira' is not found.")
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,181 @@
1
+ require "spec_helper"
2
+ require "jira/issue"
3
+
4
+ describe Jira::Issue do
5
+ describe ".initialize" do
6
+ context "when argument has 'fields' key" do
7
+ let(:issue_attributes) do
8
+ {
9
+ "id" => 1,
10
+ "jira_key" => "PRO-1",
11
+ "fields" =>
12
+ {
13
+ "summary" => "jira issue",
14
+ "project" =>
15
+ {
16
+ "key" => "FOO",
17
+ },
18
+ }
19
+ }
20
+ end
21
+
22
+ it "has @id with argument['id']" do
23
+ expect(Jira::Issue.new(issue_attributes).id).to eq issue_attributes["id"]
24
+ end
25
+
26
+ it "has @key with argument['jira_key']" do
27
+ expect(Jira::Issue.new(issue_attributes).key).to eq issue_attributes["jira_key"]
28
+ end
29
+
30
+ it "has @fields with argument['fields']" do
31
+ expect(Jira::Issue.new(issue_attributes).fields).to eq issue_attributes["fields"]
32
+ end
33
+ end
34
+
35
+ context "when argument doesn't have 'fields' key" do
36
+ let(:issue_attributes) do
37
+ {}
38
+ end
39
+
40
+ it "raises error" do
41
+ expect { Jira::Issue.new(issue_attributes) }.to raise_error
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "#[]" do
47
+ subject { Jira::Issue.new(issue_attributes)[attribute_name] }
48
+
49
+ let(:issue_attributes) do
50
+ {
51
+ "id" => "1",
52
+ "jira_key" => "FOO-1",
53
+ "fields" => {
54
+ "summary" => "jira issue",
55
+ "project" => project_attribute,
56
+ "labels" =>
57
+ [
58
+ "Feature",
59
+ "WantTo"
60
+ ],
61
+ "priority" => {
62
+ "iconUrl" => "https://jira-api/images/icon.png",
63
+ "name" => "Must",
64
+ "id" => "1"
65
+ },
66
+ "customfield_1" => nil,
67
+ }
68
+ }
69
+ end
70
+
71
+ let(:project_attribute) do
72
+ {
73
+ "key" => "FOO",
74
+ }
75
+ end
76
+
77
+ context 'id' do
78
+ let(:attribute_name) { 'id' }
79
+
80
+ it "returns issue id" do
81
+ expect(subject).to eq "1"
82
+ end
83
+ end
84
+
85
+ context 'key' do
86
+ let(:attribute_name) { 'key' }
87
+
88
+ it "returns issue key" do
89
+ expect(subject).to eq "FOO-1"
90
+ end
91
+ end
92
+
93
+ context 'summary' do
94
+ let(:attribute_name) { 'summary' }
95
+
96
+ it "returns issue summary" do
97
+ expect(subject).to eq 'jira issue'
98
+ end
99
+ end
100
+
101
+ context 'project.key' do
102
+ let(:attribute_name) { 'project.key' }
103
+
104
+ context "when project is not nil" do
105
+ it "returns issue's project key" do
106
+ expect(subject).to eq 'FOO'
107
+ end
108
+ end
109
+
110
+ context "when project is nil" do
111
+ let(:project_attribute) { nil }
112
+
113
+ it "returns nil" do
114
+ expect(subject).to be_nil
115
+ end
116
+ end
117
+ end
118
+
119
+ context 'labels' do
120
+ let(:attribute_name) { 'labels' }
121
+
122
+ it "returns issue's labels JSON string" do
123
+ expect(subject).to eq '["Feature","WantTo"]'
124
+ end
125
+ end
126
+
127
+ context 'priority' do
128
+ let(:attribute_name) { 'priority' }
129
+
130
+ it "returns issue's priority JSON string" do
131
+ expect(subject).to eq '{"iconUrl":"https://jira-api/images/icon.png","name":"Must","id":"1"}'
132
+ end
133
+ end
134
+ end
135
+
136
+ describe "#to_record" do
137
+ subject do
138
+ Jira::Issue.new(issue_attributes).to_record
139
+ end
140
+
141
+ let(:issue_attributes) do
142
+ {
143
+ "id" => 1,
144
+ "jira_key" => "FOO-1",
145
+ "fields" => {
146
+ "summary" => "jira issue",
147
+ "project" => {
148
+ "id" => "FOO",
149
+ },
150
+ "labels" =>
151
+ [
152
+ "Feature",
153
+ "WantTo"
154
+ ],
155
+ "priority" => {
156
+ "iconUrl" => "https://jira-api/images/icon.png",
157
+ "name" => "Must",
158
+ "id" => "1"
159
+ },
160
+ "customfield_1" => nil,
161
+ }
162
+ }
163
+ end
164
+
165
+ let(:expected) do
166
+ {
167
+ "id" => 1,
168
+ "key" => "FOO-1",
169
+ "summary" => "jira issue",
170
+ "project.id" => "FOO",
171
+ "labels" => "[\"Feature\",\"WantTo\"]",
172
+ "priority.name" => "Must",
173
+ "customfield_1" => "null"
174
+ }
175
+ end
176
+
177
+ it 'return guessed record' do
178
+ expect(subject).to eq expected
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,92 @@
1
+ # TODO: move to spec/jira/api_spec.rb
2
+
3
+ require "spec_helper"
4
+ require "jira/api"
5
+
6
+ describe Jira::Api do
7
+ describe ".setup" do
8
+ subject { Jira::Api.setup {} }
9
+
10
+ it "returns Jira::Api instance" do
11
+ expect(subject.is_a?(Jira::Api)).to be_truthy
12
+ end
13
+
14
+ it "calls Jiralicious.configure" do
15
+ allow(Jiralicious).to receive(:configure)
16
+ end
17
+ end
18
+
19
+ describe "#search" do
20
+ let(:jql) { "project=FOO" }
21
+
22
+ subject { Jira::Api.new.search(jql) }
23
+
24
+ it do
25
+ allow(Jiralicious).to receive(:search).with(jql)
26
+ end
27
+ end
28
+
29
+ describe "#search_issues" do
30
+ let(:jql) { "project=FOO" }
31
+ let(:results) do
32
+ [
33
+ {
34
+ "id" => 1,
35
+ "jira_key" => "FOO-1",
36
+ "fields" =>
37
+ {
38
+ "summary" => "issue summary",
39
+ "project" =>
40
+ {
41
+ "key" => "FOO"
42
+ }
43
+ }
44
+ },
45
+ {
46
+ "id" => 2,
47
+ "jira_key" => "FOO-2",
48
+ "fields" =>
49
+ {
50
+ "summary" => "jira issue",
51
+ "project" =>
52
+ {
53
+ "key" => "FOO"
54
+ }
55
+ }
56
+ }
57
+ ]
58
+ end
59
+
60
+ subject { Jira::Api.new.search_issues(jql) }
61
+
62
+ it do
63
+ allow(Jiralicious).to receive_message_chain(:search, :issues).and_return(results)
64
+
65
+ expect(subject).to be_kind_of Array
66
+ expect(subject.map(&:class)).to match_array [Jira::Issue, Jira::Issue]
67
+ end
68
+ end
69
+
70
+ describe "#total_count" do
71
+ subject { jira_api.total_count(jql) }
72
+
73
+ let(:jira_api) { Jira::Api.new }
74
+ let(:jql) { "project=FOO" }
75
+ let(:results) { Object.new } # add mock later
76
+ let(:results_count) { 5 }
77
+
78
+ before do
79
+ allow(results).to receive(:num_results).and_return(results_count)
80
+ end
81
+
82
+ it "calls Jira::Api#search with proper arguments" do
83
+ expect(jira_api).to receive(:search).with(jql, max_results: 1).and_return(results)
84
+ subject
85
+ end
86
+
87
+ it "returns issues count" do
88
+ allow(jira_api).to receive(:search).with(jql, max_results: 1).and_return(results)
89
+ expect(subject).to eq results_count
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,21 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ Bundler.require(:runtime, :development)
4
+ require "codeclimate-test-reporter"
5
+ CodeClimate::TestReporter.start
6
+
7
+ require "embulk/command/embulk_run"
8
+
9
+ Dir["./spec/support/**/*.rb"].each{|file| require file }
10
+
11
+ if ENV["COVERAGE"]
12
+ require "simplecov"
13
+ SimpleCov.start
14
+ end
15
+
16
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
17
+
18
+ RSpec.configure do |config|
19
+ config.include StdoutAndErrCapture
20
+ config.include PrepareEmbulk
21
+ end
@@ -0,0 +1,12 @@
1
+ module PrepareEmbulk
2
+ require "embulk/command/embulk_run"
3
+
4
+ classpath_dir = Embulk.home("classpath")
5
+ jars = Dir.entries(classpath_dir).select{|f| f =~ /\.jar$/ }.sort
6
+ jars.each do |jar|
7
+ require File.join(classpath_dir, jar)
8
+ end
9
+ require "embulk/java/bootstrap"
10
+
11
+ require "embulk"
12
+ end
@@ -0,0 +1,32 @@
1
+ module StdoutAndErrCapture
2
+ def capture_output(output = :out, &block)
3
+ java_import 'java.io.PrintStream'
4
+ java_import 'java.io.ByteArrayOutputStream'
5
+ java_import 'java.lang.System'
6
+
7
+ ruby_original_stream = output == :out ? $stdout.dup : $stderr.dup
8
+ java_original_stream = System.send(output) # :out or :err
9
+ ruby_buf = StringIO.new
10
+ java_buf = ByteArrayOutputStream.new
11
+
12
+ case output
13
+ when :out
14
+ $stdout = ruby_buf
15
+ when :err
16
+ $stderr = ruby_buf
17
+ end
18
+ System.setOut(PrintStream.new(java_buf))
19
+
20
+ block.call
21
+
22
+ ruby_buf.string + java_buf.toString
23
+ ensure
24
+ System.setOut(java_original_stream)
25
+ case output
26
+ when :out
27
+ $stdout = ruby_original_stream
28
+ when :err
29
+ $stderr = ruby_original_stream
30
+ end
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: embulk-input-jira
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - uu59
8
+ - yoshihara
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-05-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.5.0
20
+ name: jiralicious
21
+ prerelease: false
22
+ type: :runtime
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: 0.5.0
28
+ - !ruby/object:Gem::Dependency
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ name: bundler
35
+ prerelease: false
36
+ type: :development
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: '1.0'
42
+ - !ruby/object:Gem::Dependency
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ name: rake
49
+ prerelease: false
50
+ type: :development
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '10.0'
56
+ - !ruby/object:Gem::Dependency
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 3.2.0
62
+ name: rspec
63
+ prerelease: false
64
+ type: :development
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 3.2.0
70
+ - !ruby/object:Gem::Dependency
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ name: simplecov
77
+ prerelease: false
78
+ type: :development
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ name: pry
91
+ prerelease: false
92
+ type: :development
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ description: Loads records from Jira.
99
+ email:
100
+ - k@uu59.org
101
+ - h.yoshihara@everyleaf.com
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - .gitignore
107
+ - .travis.yml
108
+ - CHANGELOG.md
109
+ - Gemfile
110
+ - LICENSE
111
+ - README.md
112
+ - Rakefile
113
+ - VERSION
114
+ - embulk-input-jira.gemspec
115
+ - lib/embulk/input/jira-input-plugin-utils.rb
116
+ - lib/embulk/input/jira.rb
117
+ - lib/jira/api.rb
118
+ - lib/jira/issue.rb
119
+ - spec/embulk/input/jira-input-plugin-utils_spec.rb
120
+ - spec/embulk/input/jira_spec.rb
121
+ - spec/embulk_spec.rb
122
+ - spec/jira/issue_spec.rb
123
+ - spec/jira_api_spec.rb
124
+ - spec/spec_helper.rb
125
+ - spec/support/prepare_embulk.rb
126
+ - spec/support/stdout_and_err_capture.rb
127
+ homepage: https://github.com/treasure-data/embulk-input-jira
128
+ licenses:
129
+ - Apache2
130
+ metadata: {}
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - '>='
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubyforge_project:
147
+ rubygems_version: 2.4.6
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: Jira input plugin for Embulk
151
+ test_files:
152
+ - spec/embulk/input/jira-input-plugin-utils_spec.rb
153
+ - spec/embulk/input/jira_spec.rb
154
+ - spec/embulk_spec.rb
155
+ - spec/jira/issue_spec.rb
156
+ - spec/jira_api_spec.rb
157
+ - spec/spec_helper.rb
158
+ - spec/support/prepare_embulk.rb
159
+ - spec/support/stdout_and_err_capture.rb