rpruby 1.2 → 1.2.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,27 @@
1
+ require 'rake'
2
+ require 'pathname'
3
+ require 'tempfile'
4
+ require_relative '../rpruby'
5
+
6
+ namespace :rpruby do
7
+ desc 'Start launch in Report Portal and print its id to $stdout (for use with attach_to_launch formatter mode)'
8
+ task :start_launch do
9
+ description = ENV['description'] || Rpruby::Settings.instance.description
10
+ file_to_write_launch_id = ENV['file_for_launch_id'] || Rpruby::Settings.instance.file_with_launch_id
11
+ file_to_write_launch_id ||= Pathname(Dir.tmpdir) + 'rp_launch_id.tmp'
12
+ launch_id = Rpruby.start_launch(description)
13
+ File.write(file_to_write_launch_id, launch_id)
14
+ puts launch_id
15
+ end
16
+
17
+ desc 'Finish launch in Report Portal (for use with attach_to_launch formatter mode)'
18
+ task :finish_launch do
19
+ launch_id = ENV['launch_id'] || Rpruby::Settings.instance.launch_id
20
+ file_with_launch_id = ENV['file_with_launch_id'] || Rpruby::Settings.instance.file_with_launch_id
21
+ puts "Launch id isn't provided. Provide it either via RP_LAUNCH_ID or RP_FILE_WITH_LAUNCH_ID environment variables" if !launch_id && !file_with_launch_id
22
+ puts 'Both RP_LAUNCH_ID and RP_FILE_WITH_LAUNCH_ID are provided via environment variables' if launch_id && file_with_launch_id
23
+ Rpruby.launch_id = launch_id || File.read(file_with_launch_id)
24
+ Rpruby.close_child_items(nil)
25
+ Rpruby.finish_launch
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rpruby
4
+ VERSION = '1.2.1'.freeze
5
+ end
data/lib/rpruby.rb ADDED
@@ -0,0 +1,223 @@
1
+ # frozen_string_literal: true
2
+ require 'base64'
3
+ require 'cgi'
4
+ require 'http'
5
+ require 'json'
6
+ require 'mime/types'
7
+ require 'pathname'
8
+ require 'tempfile'
9
+ require 'uri'
10
+
11
+ require_relative 'rpruby/event_bus'
12
+ require_relative 'rpruby/models/item_search_options'
13
+ require_relative 'rpruby/models/test_item'
14
+ require_relative 'rpruby/settings'
15
+ require_relative 'rpruby/http_client'
16
+ require_relative "rpruby/version"
17
+
18
+ module Rpruby
19
+ class Error < StandardError; end
20
+ LOG_LEVELS = { error: 'ERROR', warn: 'WARN', info: 'INFO', debug: 'DEBUG', trace: 'TRACE', fatal: 'FATAL', unknown: 'UNKNOWN' }.freeze
21
+
22
+ class << self
23
+ attr_accessor :launch_id, :current_scenario
24
+
25
+ def now
26
+ (current_time.to_f * 1000).to_i
27
+ end
28
+
29
+ def status_to_level(status)
30
+ case status
31
+ when :passed
32
+ LOG_LEVELS[:info]
33
+ when :failed, :undefined, :pending, :error
34
+ LOG_LEVELS[:error]
35
+ when :skipped
36
+ LOG_LEVELS[:warn]
37
+ else
38
+ LOG_LEVELS.fetch(status, LOG_LEVELS[:info])
39
+ end
40
+ end
41
+
42
+ def start_launch(description, start_time = now)
43
+ required_data = { name: Settings.instance.launch, start_time: start_time, description:
44
+ description, mode: Settings.instance.launch_mode }
45
+ data = prepare_options(required_data, Settings.instance)
46
+ @launch_id = send_request(:post, 'launch', json: data)['id']
47
+ end
48
+
49
+ def finish_launch(end_time = now)
50
+ data = { end_time: end_time }
51
+ send_request(:put, "launch/#{@launch_id}/finish", json: data)
52
+ end
53
+
54
+ def start_item(item_node)
55
+ path = 'item'
56
+ path += "/#{item_node.parent.content.id}" unless item_node.parent&.is_root?
57
+ item = item_node.content
58
+ data = { start_time: item.start_time, name: item.name[0, 255], type: item.type.to_s, launch_id: @launch_id, description: item.description }
59
+ data[:tags] = item.tags unless item.tags.empty?
60
+ event_bus.broadcast(:prepare_start_item_request, request_data: data)
61
+ send_request(:post, path, json: data)['id']
62
+ end
63
+
64
+ def finish_item(item, status = nil, end_time = nil, force_issue = nil)
65
+ unless item.nil? || item.id.nil? || item.closed
66
+ data = { end_time: end_time.nil? ? now : end_time }
67
+ data[:status] = status unless status.nil?
68
+ if force_issue && status != :passed # TODO: check for :passed status is probably not needed
69
+ data[:issue] = { issue_type: 'AUTOMATION_BUG', comment: force_issue.to_s }
70
+ elsif status == :skipped
71
+ data[:issue] = { issue_type: 'NOT_ISSUE' }
72
+ end
73
+ send_request(:put, "item/#{item.id}", json: data)
74
+ item.closed = true
75
+ end
76
+ end
77
+
78
+ # TODO: implement force finish
79
+
80
+ def send_log(status, message, time)
81
+ unless @current_scenario.nil? || @current_scenario.closed # it can be nil if scenario outline in expand mode is executed
82
+ data = { item_id: @current_scenario.id, time: time, level: status_to_level(status), message: message.to_s }
83
+ send_request(:post, 'log', json: data)
84
+ end
85
+ end
86
+
87
+ def send_file(status, path_or_src, label = nil, time = now, mime_type = 'image/png')
88
+ str_without_nils = path_or_src.to_s.gsub("\0", '') # file? does not allow NULLs inside the string
89
+ if File.file?(str_without_nils)
90
+ send_file_from_path(status, path_or_src, label, time, mime_type)
91
+ else
92
+ if mime_type =~ /;base64$/
93
+ mime_type = mime_type[0..-8]
94
+ path_or_src = Base64.decode64(path_or_src)
95
+ end
96
+ extension = ".#{MIME::Types[mime_type].first.extensions.first}"
97
+ Tempfile.open(['report_portal', extension]) do |tempfile|
98
+ tempfile.binmode
99
+ tempfile.write(path_or_src)
100
+ tempfile.rewind
101
+ send_file_from_path(status, tempfile.path, label, time, mime_type)
102
+ end
103
+ end
104
+ end
105
+
106
+ # @option options [Hash] options, see ReportPortal::ItemSearchOptions
107
+ def get_items(filter_options = {})
108
+ page_size = 100
109
+ max_pages = 100
110
+ all_items = []
111
+ 1.step.each do |page_number|
112
+ raise 'Too many pages with the results were returned' if page_number > max_pages
113
+
114
+ options = ItemSearchOptions.new({ page_size: page_size, page_number: page_number }.merge(filter_options))
115
+ page_items = send_request(:get, 'item', params: options.query_params)['content'].map do |item_params|
116
+ TestItem.new(item_params)
117
+ end
118
+ all_items += page_items
119
+ break if page_items.size < page_size
120
+ end
121
+ all_items
122
+ end
123
+
124
+ # @param item_ids [Array<String>] an array of items to remove (represented by ids)
125
+ def delete_items(item_ids)
126
+ send_request(:delete, 'item', params: { ids: item_ids })
127
+ end
128
+
129
+ # needed for parallel formatter
130
+ def item_id_of(name, parent_node)
131
+ path = if parent_node.is_root? # folder without parent folder
132
+ "item?filter.eq.launch=#{@launch_id}&filter.eq.name=#{CGI.escape(name)}&filter.size.path=0"
133
+ else
134
+ "item?filter.eq.parent=#{parent_node.content.id}&filter.eq.name=#{CGI.escape(name)}"
135
+ end
136
+ data = send_request(:get, path)
137
+ if data.key? 'content'
138
+ data['content'].empty? ? nil : data['content'][0]['id']
139
+ end
140
+ end
141
+
142
+ # needed for parallel formatter
143
+ def launch_id_to_number()
144
+ path = "launch/#{@launch_id}"
145
+ data = send_request(:get, path)
146
+ data['id']
147
+ end
148
+
149
+ # needed for parallel formatter
150
+ def close_child_items(parent_id)
151
+ path = if parent_id.nil?
152
+ "item?filter.eq.launchId=#{launch_id_to_number}"
153
+ else
154
+ "item?filter.eq.parent=#{parent_id}&page.page=1&page.size=100"
155
+ end
156
+ ids = []
157
+ loop do
158
+ data = send_request(:get, path)
159
+ if data.key?('links')
160
+ link = data['links'].find { |i| i['rel'] == 'next' }
161
+ url = link.nil? ? nil : link['href']
162
+ else
163
+ url = nil
164
+ end
165
+ data['content'].each do |i|
166
+ ids << i['id'] if i['has_childs'] && i['status'] == 'IN_PROGRESS'
167
+ end
168
+ break if url.nil?
169
+ end
170
+
171
+ ids.each do |id|
172
+ close_child_items(id)
173
+ finish_item(TestItem.new(id: id))
174
+ end
175
+ end
176
+
177
+ # Registers an event. The proc will be called back with the event object.
178
+ def on_event(name, &proc)
179
+ event_bus.on(name, &proc)
180
+ end
181
+
182
+ private
183
+
184
+ def send_file_from_path(status, path, label, time, mime_type)
185
+ File.open(File.realpath(path), 'rb') do |file|
186
+ filename = File.basename(file)
187
+ json = [{ level: status_to_level(status), message: label || filename, item_id: @current_scenario.id, time: time, file: { name: filename } }]
188
+ form = {
189
+ json_request_part: HTTP::FormData::Part.new(JSON.dump(json), content_type: 'application/json'),
190
+ binary_part: HTTP::FormData::File.new(file, filename: filename, content_type: MIME::Types[mime_type].first.to_s)
191
+ }
192
+ send_request(:post, 'log', form: form)
193
+ end
194
+ end
195
+
196
+ def send_request(verb, path, options = {})
197
+ http_client.send_request(verb, path, options)
198
+ end
199
+
200
+ def http_client
201
+ @http_client ||= HttpClient.new
202
+ end
203
+
204
+ def current_time
205
+ # `now_without_mock_time` is provided by Timecop and returns a real, not mocked time
206
+ return Time.now_without_mock_time if Time.respond_to?(:now_without_mock_time)
207
+
208
+ Time.now
209
+ end
210
+
211
+ def event_bus
212
+ @event_bus ||= EventBus.new
213
+ end
214
+
215
+ def prepare_options(data, config = {})
216
+ if config.attributes
217
+ data[:attributes] = config.attributes
218
+ elsif (data[:tags] = config.tags)
219
+ end
220
+ data
221
+ end
222
+ end
223
+ end
data/rpruby.gemspec ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ # lib = File.expand_path('lib', __dir__)
3
+ # $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require_relative "lib/rpruby/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "rpruby"
9
+ spec.version = Rpruby::VERSION
10
+ spec.authors = ["Jayachandra Konduru(JC)"]
11
+ spec.email = ["jayak8390@gmail.com"]
12
+
13
+ spec.summary = 'ReportPortal Ruby Client'
14
+ spec.description = 'Cucumber and RSpec clients for EPAM ReportPortal system'
15
+ spec.homepage = 'https://github.com/jaya-konduru/rpruby'
16
+ spec.license = "MIT"
17
+ spec.required_ruby_version = ">= 2.4.0"
18
+
19
+ #spec.metadata["allowed_push_host"] = "TODO: Set to 'https://mygemserver.com'"
20
+
21
+ #spec.metadata["homepage_uri"] = spec.homepage
22
+ #spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
23
+ #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
24
+
25
+ # Specify which files should be added to the gem when it is released.
26
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
28
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
29
+ end
30
+ spec.bindir = "bin"
31
+ spec.executables = spec.files.grep(%r{\Abin/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ # Uncomment to register a new dependency of your gem
35
+ # spec.add_dependency "example-gem", "~> 1.0"
36
+
37
+ spec.add_dependency('http')
38
+ spec.add_dependency('mime-types')
39
+ spec.add_dependency('rubytree')
40
+ spec.add_development_dependency('rubocop')
41
+
42
+ # For more information and examples about making a new gem, checkout our
43
+ # guide at: https://bundler.io/guides/creating_gem.html
44
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rpruby
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.2'
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jayachandra Konduru(JC)
@@ -69,10 +69,45 @@ dependencies:
69
69
  description: Cucumber and RSpec clients for EPAM ReportPortal system
70
70
  email:
71
71
  - jayak8390@gmail.com
72
- executables: []
72
+ executables:
73
+ - console
74
+ - setup
73
75
  extensions: []
74
76
  extra_rdoc_files: []
75
- files: []
77
+ files:
78
+ - ".DS_Store"
79
+ - ".github/workflows/main.yml"
80
+ - ".gitignore"
81
+ - ".rspec"
82
+ - ".rubocop.yml"
83
+ - CHANGELOG.md
84
+ - CODE_OF_CONDUCT.md
85
+ - Gemfile
86
+ - LICENSE.txt
87
+ - README.md
88
+ - Rakefile
89
+ - bin/console
90
+ - bin/setup
91
+ - config/report_portal.yaml.example
92
+ - lib/rpruby.rb
93
+ - lib/rpruby/cucumber/formatter.rb
94
+ - lib/rpruby/cucumber/messagereport.rb
95
+ - lib/rpruby/cucumber/parallel_formatter.rb
96
+ - lib/rpruby/cucumber/parallel_report.rb
97
+ - lib/rpruby/cucumber/report.rb
98
+ - lib/rpruby/event_bus.rb
99
+ - lib/rpruby/events/prepare_start_item_request.rb
100
+ - lib/rpruby/http_client.rb
101
+ - lib/rpruby/logging/log4r_outputter.rb
102
+ - lib/rpruby/logging/logger.rb
103
+ - lib/rpruby/logging/logging_appender.rb
104
+ - lib/rpruby/models/item_search_options.rb
105
+ - lib/rpruby/models/test_item.rb
106
+ - lib/rpruby/rspec/formatter.rb
107
+ - lib/rpruby/settings.rb
108
+ - lib/rpruby/tasks.rb
109
+ - lib/rpruby/version.rb
110
+ - rpruby.gemspec
76
111
  homepage: https://github.com/jaya-konduru/rpruby
77
112
  licenses:
78
113
  - MIT