bizside-redmine-client 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ccbf9316e52bbc4741788170d4b8df1861739b05
4
+ data.tar.gz: 701e5f7b1b3cb36d87aea56fbcbd048762769b10
5
+ SHA512:
6
+ metadata.gz: 200fd08c22f22e6e462b5af2c45d2671f87985114cfb1f817abff160c70590874419334e182b616997d087dfb6b4b28ae30004a8cd980127d17943aaec8516d0
7
+ data.tar.gz: 1d27984ab1e0c33d7a5b447b85d07ef121f7e05308a11bfcfa727246fea83cd9de391dbd02cb8b1fcdd4f912b02241b2468140bbc64145c25fea161643a2fb6d
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ Gemfile.lock
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at atsushi.inoue@probizmo.co.jp. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in bizside-redmine-client.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 atsushi.inoue
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Bizside::Redmine::Client
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/bizside/redmine/client`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'bizside-redmine-client'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install bizside-redmine-client
22
+
23
+ ## Usage
24
+
25
+ ```ruby
26
+ require 'bizside/redmine/client'
27
+
28
+ client = Bizside::Redmine::Client.new(host: 'your-redmine-host', api_key: 'your-redmine-account-api-key')
29
+ projects = client.projects
30
+ projects.each {|project| puts "ID: #{project['id']}, Name: #{project['name']}" } && nil
31
+ ```
32
+
33
+ The client object also has some other methods to get Redmine data or post data to Redmine.
34
+
35
+ ## Development
36
+
37
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
38
+
39
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
40
+
41
+ ## Contributing
42
+
43
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/bizside-redmine-client. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
44
+
45
+ ## License
46
+
47
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
48
+
49
+ ## Code of Conduct
50
+
51
+ Everyone interacting in the Bizside::Redmine::Client project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/bizside-redmine-client/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "bizside/redmine/client"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,33 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "bizside/redmine/client/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "bizside-redmine-client"
7
+ spec.version = Bizside::Redmine::Client::VERSION
8
+ spec.authors = ["y-matsuda"]
9
+ spec.email = ["matsuda@lab.acs-jp.com"]
10
+
11
+ spec.summary = %q{redmine client for bizside}
12
+ spec.description = %q{operate redmine server using api request in bizside}
13
+ spec.homepage = "https://github.com/maedadev/bizside-redmine-client"
14
+ spec.license = "MIT"
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_dependency 'faraday', '~> 0.12'
26
+ spec.add_dependency 'activesupport', '>= 3.2', '< 6.0.0'
27
+ spec.add_dependency 'nokogiri', '~> 1.10'
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.17"
30
+ spec.add_development_dependency "rake", "~> 10.0"
31
+ spec.add_development_dependency "minitest", "~> 5.0"
32
+ spec.add_development_dependency 'webmock', '~> 3.0'
33
+ end
@@ -0,0 +1,214 @@
1
+ require "bizside/redmine/client/version"
2
+ require "active_support/all"
3
+ require "bizside/redmine/connection"
4
+ require "bizside/redmine/result_set"
5
+
6
+ require "bizside/redmine/text_to_textile"
7
+ require "bizside/redmine/html_to_textile"
8
+
9
+ class Bizside::Redmine::Client
10
+ DEFAULT_CONFIG = {
11
+ host: 'localhost',
12
+ prefix: '/',
13
+ api_key: '',
14
+ verify_ssl: false,
15
+ }.freeze
16
+
17
+ cattr_accessor :logger, instance_accessor: false
18
+ cattr_reader :config, instance_accessor: false
19
+ attr_reader :prefix
20
+
21
+ @@config = DEFAULT_CONFIG.dup
22
+
23
+ def self.set_config(config)
24
+ @@config = DEFAULT_CONFIG.dup
25
+ DEFAULT_CONFIG.keys.each do |key|
26
+ @@config[key] = config[key] unless config[key].nil?
27
+ end
28
+ end
29
+
30
+ def initialize(overrides = {})
31
+ overrides = overrides.symbolize_keys
32
+ if logger = overrides.delete(:logger)
33
+ @@logger = logger
34
+ end
35
+
36
+ @prefix = overrides[:prefix] || Bizside::Redmine::Client.config[:prefix]
37
+ @connection = Bizside::Redmine::Connection.new(overrides)
38
+ end
39
+
40
+ def projects(params = {})
41
+ params = params.symbolize_keys
42
+
43
+ api_params = {
44
+ include: 'trackers,issue_categories',
45
+ page: params[:page] || 1,
46
+ per: params[:per] || 100
47
+ }
48
+
49
+ response = connection.get("#{prefix}/projects.json", api_params)
50
+ decode(:projects, response)
51
+ end
52
+
53
+ def trackers
54
+ response = connection.get("#{prefix}/trackers.json")
55
+ decode(:trackers, response)
56
+ end
57
+
58
+ def wiki_pages(project_identifier)
59
+ response = connection.get("#{prefix}/projects/#{project_identifier}/wiki/index.json")
60
+ decode(:wiki_pages, response)
61
+ end
62
+
63
+ def create_issue(params)
64
+ params = params.symbolize_keys
65
+ issue_params = params[:issue].symbolize_keys
66
+
67
+ api_params = {
68
+ :issue => {
69
+ :project_id => issue_params[:project_id],
70
+ :subject => issue_params[:subject],
71
+ }
72
+ }
73
+ api_params[:issue][:tracker_id] = issue_params[:tracker_id] if issue_params[:tracker_id]
74
+ api_params[:issue][:status_id] = issue_params[:status_id] if issue_params[:status_id]
75
+ api_params[:issue][:priority_id] = issue_params[:priority_id] if issue_params[:priority_id]
76
+ api_params[:issue][:description] = issue_params[:description] if issue_params[:description]
77
+ api_params[:issue][:category_id] = issue_params[:category_id] if issue_params[:category_id]
78
+ api_params[:issue][:fixed_version_id] = issue_params[:fixed_version_id] if issue_params[:fixed_version_id]
79
+ api_params[:issue][:assigned_to_id] = issue_params[:assigned_to_id] if issue_params[:assigned_to_id]
80
+ api_params[:issue][:parent_issue_id] = issue_params[:parent_issue_id] if issue_params[:parent_issue_id]
81
+ api_params[:issue][:custom_fields] = issue_params[:custom_fields] if issue_params[:custom_fields]
82
+ api_params[:issue][:watcher_user_ids] = issue_params[:watcher_user_ids] if issue_params[:watcher_user_ids]
83
+ api_params[:issue][:uploads] = issue_params[:uploads] if issue_params[:uploads]
84
+
85
+ response = connection.post("#{prefix}/issues.json", api_params)
86
+ decode(:issue, response)
87
+ end
88
+
89
+ def update_issue(id, params)
90
+ params = params.symbolize_keys
91
+ issue_params = params[:issue].symbolize_keys
92
+
93
+ api_params = {
94
+ :issue => {
95
+ :notes => issue_params[:notes],
96
+ }
97
+ }
98
+ api_params[:issue][:uploads] = issue_params[:uploads] if issue_params[:uploads]
99
+
100
+ response = connection.put("#{prefix}/issues/#{id}.json", api_params)
101
+ decode(:issue, response)
102
+ end
103
+
104
+ def create_wiki_page(params)
105
+ path = "#{prefix}/projects/#{params[:project_identifier]}/wiki/#{params[:page_name]}.xml"
106
+ content = "<wiki_page><text>#{params[:content]}</text></wiki_page>"
107
+ response = connection.post_or_put(path, content)
108
+ decode(:wiki_page, response)
109
+ end
110
+
111
+ def create_analyzed_wiki_pages(params)
112
+ content = "<wiki_page><text>#{params[:content]}</text></wiki_page>"
113
+ response = connection.post_or_put("#{prefix}/projects/#{params[:project_identifier]}/wiki/#{params[:page_name]}.xml", content)
114
+ decode(:wiki_page, response)
115
+ end
116
+
117
+ def create_errors_wiki_pages(params)
118
+ content = "<wiki_page><text>h3. #{params[:page_name]} <notextile></notextile>&lt;pre&gt;#{params[:content]}&lt;/pre&gt;</text></wiki_page>"
119
+ response = connection.post_or_put("#{prefix}/projects/#{params[:project_identifier]}/wiki/#{params[:page_name]}.xml", content)
120
+ decode(:wiki_page, response)
121
+ end
122
+
123
+ def create_aggregated_wiki_page(params)
124
+ params.each do |project_identifier, hash|
125
+ hash.each do |env, page_names|
126
+ year_months = page_names.sort.reverse.join(" ").gsub(" ", "\n")
127
+ content = "<wiki_page><text>#{year_months}</text></wiki_page>"
128
+ connection.post_or_put("#{prefix}/projects/#{project_identifier}/wiki/#{env}-result-analyzer.xml", content)
129
+ end
130
+ end
131
+ end
132
+
133
+ def create_year_month_wiki_pages(params)
134
+ params.each do |project_identifier, hash|
135
+ hash.each do |year_month, page_names|
136
+ page_names = page_names.sort.reverse.join(" ").gsub(" ", "\n")
137
+ content = "<wiki_page><text>#{page_names}</text></wiki_page>"
138
+ connection.post_or_put("#{prefix}/projects/#{project_identifier}/wiki/#{year_month}.xml", content)
139
+ end
140
+ end
141
+ end
142
+
143
+ def upload_file(params)
144
+ options = params.symbolize_keys
145
+ api_params = {
146
+ :file => options[:file]
147
+ }
148
+ response = connection.post_with_multipart("#{prefix}/uploads.json", api_params)
149
+ decode(:upload, response)
150
+ end
151
+
152
+ def dms_folders(params)
153
+ params = params.symbolize_keys
154
+
155
+ api_params = {}
156
+ api_params[:folder_id] = params[:folder_id] if params[:folder_id].present?
157
+
158
+ response = connection.get("#{prefix}/projects/#{params[:project_id]}/dmsf.json", api_params)
159
+ decode("", response)
160
+ end
161
+
162
+ def dms_create_folder(params)
163
+ params = params.symbolize_keys
164
+
165
+ api_params = {
166
+ dmsf_folder: {
167
+ title: params[:title],
168
+ description: params[:description].present? ? params[:description] : '-'
169
+ }
170
+ }
171
+ api_params[:dmsf_folder_id] = params[:parent_folder_id] if params[:parent_folder_id].present?
172
+
173
+ response = connection.post("#{prefix}/projects/#{params[:project_id]}/dmsf/create.json", api_params)
174
+ decode("", response)
175
+ end
176
+
177
+ def dms_upload_file(params)
178
+ params = params.symbolize_keys
179
+ api_params = {
180
+ :file => params[:file]
181
+ }
182
+ filename = File.basename(params[:file])
183
+ response = connection.post_with_multipart("#{prefix}/projects/#{params[:project_id]}/dmsf/upload.json?filename=#{filename}", api_params)
184
+ decode('', response)
185
+ end
186
+
187
+ def dms_commit_file(params)
188
+ params = params.symbolize_keys
189
+ api_params = {
190
+ attachments: {
191
+ folder_id: params[:folder_id],
192
+ uploaded_file: {
193
+ name: params[:name],
194
+ title: params[:title],
195
+ description: params[:description].present? ? params[:description] : '-',
196
+ comment: params[:comment].present? ? params[:comment] : '-',
197
+ token: params[:token]
198
+ }
199
+ }
200
+ }
201
+ response = connection.post("#{prefix}/projects/#{params[:project_id]}/dmsf/commit.json", api_params)
202
+ decode("", response)
203
+ end
204
+
205
+ private
206
+
207
+ def connection
208
+ @connection
209
+ end
210
+
211
+ def decode(key, response)
212
+ Bizside::Redmine::ResultSet.new(key, response.status, response.body)
213
+ end
214
+ end
@@ -0,0 +1,7 @@
1
+ module Bizside
2
+ module Redmine
3
+ class Client
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,95 @@
1
+ require 'faraday'
2
+
3
+ class Bizside::Redmine::Connection
4
+
5
+ attr_reader :host, :api_key, :verify_ssl
6
+
7
+ def initialize(overrides = {})
8
+ @host = overrides[:host] || Bizside::Redmine::Client.config[:host]
9
+ @api_key = overrides[:api_key] || Bizside::Redmine::Client.config[:api_key]
10
+ @verify_ssl = overrides.has_key?(:verify_ssl) ?
11
+ overrides[:verify_ssl] :
12
+ Bizside::Redmine::Client.config[:verify_ssl]
13
+ end
14
+
15
+ def get(path, params = {})
16
+ begin
17
+ connection.get(path, params) do |request|
18
+ request.headers['X-Redmine-API-Key'] = api_key
19
+ end
20
+ rescue Faraday::ConnectionFailed
21
+ raise 'Could not connect to the Redmine server.'
22
+ end
23
+ end
24
+
25
+ def post(path, params = {})
26
+ begin
27
+ connection.post(path) do |request|
28
+ request.headers['X-Redmine-API-Key'] = api_key
29
+ request.headers['Content-Type'] = 'application/json'
30
+ request.body = params.to_json
31
+ end
32
+ rescue Faraday::ConnectionFailed
33
+ raise 'Could not connect to the Redmine server.'
34
+ end
35
+ end
36
+
37
+ def put(path, params = {})
38
+ begin
39
+ connection.put(path) do |request|
40
+ request.headers['X-Redmine-API-Key'] = api_key
41
+ request.headers['Content-Type'] = 'application/json'
42
+ request.body = params.to_json
43
+ end
44
+ rescue Faraday::ConnectionFailed
45
+ raise 'Could not connect to the Redmine server.'
46
+ end
47
+ end
48
+
49
+ def post_or_put(path, content)
50
+ begin
51
+ connection.put(path) do |request|
52
+ request.headers['X-Redmine-API-Key'] = api_key
53
+ request.headers['Content-Type'] = 'application/xml'
54
+ request.body = content
55
+ end
56
+ rescue Faraday::ConnectionFailed
57
+ raise 'Could not connect to the Redmine server.'
58
+ end
59
+ end
60
+
61
+ def post_with_multipart(path, params = {})
62
+ begin
63
+ connection(:multipart => true).post(path) do |request|
64
+ request.headers['X-Redmine-API-Key'] = api_key
65
+ request.headers['Content-Type'] = 'application/octet-stream'
66
+ request.body = File.binread(params[:file].path)
67
+ end
68
+ rescue Faraday::ConnectionFailed
69
+ raise 'Could not connect to the Redmine server.'
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def connection(options = {})
76
+ Faraday.new(:url => 'https://' + host, :ssl => ssl_options) do |faraday|
77
+ if options[:multipart]
78
+ faraday.request :multipart
79
+ else
80
+ faraday.request :url_encoded
81
+ end
82
+ faraday.adapter Faraday.default_adapter
83
+ end
84
+ end
85
+
86
+ def ssl_options
87
+ ssl_dir = File.expand_path(File.join(File.dirname(File.dirname(File.dirname(__FILE__))), 'ssl'))
88
+ {
89
+ :ca_path => ssl_dir,
90
+ :ca_file => File.join(ssl_dir, 'cert.pem'),
91
+ :verify => verify_ssl,
92
+ }
93
+ end
94
+
95
+ end
@@ -0,0 +1,207 @@
1
+ # Refer to https://github.com/jystewart/html2textile
2
+ require 'cgi'
3
+ require 'nokogiri'
4
+
5
+ class Bizside::Redmine::HtmlToTextile
6
+ NEWLINE = "\n"
7
+
8
+ # SAX parser
9
+ class Converter < Nokogiri::XML::SAX::Document
10
+ # Tag translations
11
+
12
+ # Maps tokens for opening and closing tags
13
+ SHARED_TAGS = {
14
+ # Text formatting
15
+ :b => '**',
16
+ :strong => '*',
17
+ }.freeze
18
+
19
+ # Maps tokens for opening tags
20
+ OPENING_TAGS = SHARED_TAGS.dup.update(
21
+ # Headings
22
+ :table => 'h2. ',
23
+ :h1 => "\n h1. ",
24
+ :h2 => 'h2. ',
25
+
26
+ # Tables
27
+ :th => '|_.',
28
+ :td => '|',
29
+
30
+ # Special
31
+ :a => '"',
32
+
33
+ # Structures
34
+ :p => 'p. ',
35
+ :br => NEWLINE,
36
+ ).freeze
37
+
38
+ # Maps tokens for closing tags
39
+ CLOSING_TAGS = SHARED_TAGS.dup.update(
40
+ # Tables
41
+ :tr => '|',
42
+ :td => ' ',
43
+ :th => ' ',
44
+
45
+ # Special
46
+ :a => '":',
47
+ ).freeze
48
+
49
+ # Typical block elements
50
+ BLOCK = [:h1, :h2, :p, :div, :table].freeze
51
+
52
+ # This is kinda a special case for block elements
53
+ ROW = [:tr, :li].freeze
54
+
55
+ # Note that th/td in Textile are sort of inline despite truly being block
56
+ INLINE = [:b, :strong, :span, :a, :th, :td].freeze
57
+
58
+ attr_reader :converted, :original, :stack
59
+
60
+ def initialize
61
+ @converted = ''
62
+ @stack = []
63
+ end
64
+
65
+ # Opening tag callback
66
+ def start_element(tag_name, attributes = [])
67
+ # Preprocess, and push to stack
68
+ element = tag_name.downcase.to_sym
69
+ attribs = Hash[attributes]
70
+ opening = OPENING_TAGS[element].to_s.dup
71
+ styling = prepare_styles(attribs, element)
72
+ spaces = spacing(element)
73
+ stack << [element, attribs]
74
+
75
+ # Styling info gets positioned depending on element type
76
+ content = case
77
+ when BLOCK.include?(element)
78
+ opening.sub('.', styling + '.')
79
+ when ROW.include?(element)
80
+ (styling.empty?) ? opening + ' ' : opening + styling + ('.' if :td == element).to_s + ' '
81
+ else opening + styling
82
+ end
83
+
84
+ # add white space & content
85
+ append_white(spaces)
86
+ converted << content
87
+ end
88
+
89
+ # Closing tag callback
90
+ def end_element(tag_name)
91
+ element, attribs = stack.pop
92
+ spaces = spacing(element)
93
+ closing = CLOSING_TAGS[element].to_s
94
+
95
+ # Deal with cases for a/img
96
+ converted << case element
97
+ when :a
98
+ special_ending(attribs['title']) + closing + attribs['href'].to_s
99
+ else closing
100
+ end
101
+ append_white(spaces)
102
+ end
103
+
104
+ # Normal character stream
105
+ def characters(text)
106
+ # # Newlines should not be treated like <br /> tags, however don't indent
107
+ # on new lines so consume any preceeding whitespace
108
+ content = CGI.unescapeHTML(text).gsub(NEWLINE, ' ')
109
+ content.rstrip! if content.ends_with? NEWLINE
110
+ content.lstrip! if converted.ends_with? NEWLINE
111
+ converted << content
112
+ end
113
+
114
+ private
115
+
116
+ # Put white space at the end, but only if required
117
+ def append_white(spacing)
118
+ (- spacing.size).upto(-1) do |i|
119
+ space, last = spacing[i], converted[i]
120
+ converted << space unless space == last or NEWLINE == last
121
+ end
122
+ end
123
+
124
+ # Create styles, id, CSS classes, colspans, padding
125
+ def prepare_styles(attribs, element)
126
+ styling = attribs['class'].to_s.split(/\s+/)
127
+ styling << '#' + attribs['id'] unless attribs['id'].blank?
128
+ [].tap do |items|
129
+ styles = attribs['style'].to_s.dup.strip
130
+ unless styles.blank?
131
+ items << '{%s}' % styles unless styles.blank?
132
+ end
133
+ items << '(%s)' % styling.join(' ') unless styling.empty?
134
+ end.join
135
+ end
136
+
137
+ # For special case closing tags (a and img)
138
+ def special_ending(text)
139
+ (text.present?) ? '(%s)' % text : ''
140
+ end
141
+
142
+ # Get spacing gap for a tag
143
+ def spacing(element)
144
+ return NEWLINE * 2 if BLOCK.include? element
145
+ return NEWLINE if ROW.include? element
146
+ ''
147
+ end
148
+ end
149
+
150
+ # Wrapper for SAX parser
151
+ def self.convert(text)
152
+ # Note, start-of-line is white space trimmed and we use HTML parsing to wrap up a fake HTML root node
153
+ text.gsub!(/<title.*title>/, '')
154
+ text.gsub!(/<style.*style>/m, '')
155
+ text.gsub!(/class="rla-report-table" cellspacing="0"/, '')
156
+ text.gsub!(/class="alt"/, '')
157
+ mark_up = text.gsub(/\n\ +/, NEWLINE).gsub(/\>\s*\n/, '> ')
158
+ converter = Converter.new
159
+ Nokogiri::HTML::SAX::Parser.new(converter).parse(mark_up)
160
+ converter.converted.strip
161
+ end
162
+
163
+ def self.tweak_wiki_style(textile_format)
164
+ content = textile_format.gsub(/\n\n\s\|\n/, "\n")
165
+ content.gsub!(/^h2\. Routing Errors$/, "h2. Routing Errors \n")
166
+ content.gsub!(/^h2\. Parse warnings$/, "\n h2. Parse warnings \n")
167
+ content.gsub!(/^h2\. Thanks for using request-log-analyzer$/, "--- \n\n h2. Thanks for using request-log-analyzer")
168
+ content.gsub!(/\n\s\|_\./, "\n{background: #CAE8EA}. |_.")
169
+
170
+ # Get threshold to draw color only Mean & StdDev
171
+ threshold = ENV['THRESHOLD'] || 400
172
+ colorized_content = ''
173
+ content.each_line do |line|
174
+ splited = line.split('|')
175
+
176
+ if splited[4].present?
177
+ mean = splited[4].strip
178
+ if mean.match(/^\d+ms$/)
179
+ if mean.to_i > threshold.to_i
180
+ splited[4] = mean.strip.gsub(/^\d+ms$/, "%{color:red} #{mean}%")
181
+ end
182
+ elsif mean.match(/^\d+\.\d+s$/)
183
+ if mean.to_f * 1000 > threshold.to_i
184
+ splited[4] = mean.strip.gsub(/^\d+\.\d+s$/, "%{color:red} #{mean}%")
185
+ end
186
+ end
187
+ end
188
+
189
+ if splited[5].present?
190
+ std_dev = splited[5].strip
191
+ if std_dev.match(/^\d+ms$/)
192
+ if std_dev.to_i > threshold.to_i
193
+ splited[5] = std_dev.gsub(/^\d+ms$/, "%{color:red} #{std_dev}%")
194
+ end
195
+ elsif std_dev.match(/^\d+\.\d+s$/)
196
+ if std_dev.to_f * 1000 > threshold.to_i
197
+ splited[5] = std_dev.strip.gsub(/^\d+\.\d+s$/, "%{color:red} #{std_dev}%")
198
+ end
199
+ end
200
+ end
201
+
202
+ colorized_content << splited.join("|")
203
+ end
204
+ colorized_content
205
+ end
206
+
207
+ end
@@ -0,0 +1,55 @@
1
+ class Bizside::Redmine::ResultSet
2
+ include Enumerable
3
+
4
+ attr_reader :key
5
+ attr_reader :status
6
+ attr_reader :rows
7
+ attr_reader :errors
8
+
9
+ def initialize(key, status, json_string = nil)
10
+ @key = key
11
+ @status = status
12
+
13
+ if json_string.present?
14
+ json = ActiveSupport::JSON.decode(json_string.force_encoding('UTF-8'))
15
+ @rows = key.present? ? json[key.to_s] : json
16
+ @errors = json['errors']
17
+ else
18
+ warn(status)
19
+ end
20
+
21
+ @rows ||= []
22
+ end
23
+
24
+ def ==(target)
25
+ self.key == target.key &&
26
+ self.status == target.status &&
27
+ self.rows == target.rows &&
28
+ self.errors == target.errors
29
+ end
30
+
31
+ def each
32
+ rows.each do |hash|
33
+ yield hash
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def warn(status)
40
+ case status
41
+ when 200
42
+ return
43
+ when 401
44
+ message = "[Redmine] HTTP#{status}: API authentication failed."
45
+ else
46
+ message = "[Redmine] HTTP#{status}: An error has occurred."
47
+ end
48
+
49
+ if Bizside::Redmine::Client.logger
50
+ Bizside::Redmine::Client.logger.warn message
51
+ else
52
+ puts message
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+ class Bizside::Redmine::TextToTextile
2
+
3
+ # NOTE: refs #16729 special symbol for xml
4
+ def self.convert(text)
5
+ text.gsub!('<', '&lt;')
6
+ text.gsub!('>', '&gt;')
7
+ text.gsub!('&', '&amp;')
8
+ text
9
+ end
10
+
11
+ end
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bizside-redmine-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - y-matsuda
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-06-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.12'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '3.2'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: 6.0.0
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '3.2'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: 6.0.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: nokogiri
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.10'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.10'
61
+ - !ruby/object:Gem::Dependency
62
+ name: bundler
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.17'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.17'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '10.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '10.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: minitest
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '5.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '5.0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: webmock
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.0'
117
+ description: operate redmine server using api request in bizside
118
+ email:
119
+ - matsuda@lab.acs-jp.com
120
+ executables: []
121
+ extensions: []
122
+ extra_rdoc_files: []
123
+ files:
124
+ - ".gitignore"
125
+ - CODE_OF_CONDUCT.md
126
+ - Gemfile
127
+ - LICENSE.txt
128
+ - README.md
129
+ - Rakefile
130
+ - bin/console
131
+ - bin/setup
132
+ - bizside-redmine-client.gemspec
133
+ - lib/bizside/redmine/client.rb
134
+ - lib/bizside/redmine/client/version.rb
135
+ - lib/bizside/redmine/connection.rb
136
+ - lib/bizside/redmine/html_to_textile.rb
137
+ - lib/bizside/redmine/result_set.rb
138
+ - lib/bizside/redmine/text_to_textile.rb
139
+ homepage: https://github.com/maedadev/bizside-redmine-client
140
+ licenses:
141
+ - MIT
142
+ metadata: {}
143
+ post_install_message:
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 2.5.2.3
160
+ signing_key:
161
+ specification_version: 4
162
+ summary: redmine client for bizside
163
+ test_files: []