embulk-input-jira 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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